AVCaptureVideoPreviewLayer ориентация - нужна альбомная ориентация

Мое приложение работает только в альбомной ориентации. Я представляю AVCaptureVideoPreviewLayer следующим образом:

self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
[self.previewLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
[self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];                    
NSLog(@"previewView: %@", self.previewView);
CALayer *rootLayer = [self.previewView layer];
[rootLayer setMasksToBounds:YES];
[self.previewLayer setFrame:[rootLayer bounds]];
    NSLog(@"previewlayer: %f, %f, %f, %f", self.previewLayer.frame.origin.x, self.previewLayer.frame.origin.y, self.previewLayer.frame.size.width, self.previewLayer.frame.size.height);
[rootLayer addSublayer:self.previewLayer];
[session startRunning];

self.previewView имеет фрейм (0,0,568,320), что правильно. self.previewLayer регистрирует кадр (0,0,568,320), что теоретически верно. Однако дисплей камеры выглядит как портретный прямоугольник в середине экрана с альбомной ориентацией, а ориентация изображения предварительного просмотра камеры неверна на 90 градусов. Что я делаю неправильно? Мне нужно, чтобы слой предварительного просмотра камеры отображался в полноэкранном режиме в альбомном режиме, а изображение должно быть правильно ориентировано.


person soleil    schedule 25.02.2013    source источник
comment
Внимание! Для 2016 года перейдите к правильному ответу ниже stackoverflow.com/a/36575423/294884 Как и во многих других областях вычислительной техники, детали API меняются с годами. С autolayout и т. Д. Старые ответы здесь (которые были превосходными в то время) сейчас не подходят. (Еще через три-четыре года будет новый правильный ответ!)   -  person Fattie    schedule 20.05.2016


Ответы (19)


Ориентация камеры по умолчанию - Альбомная влево (кнопка «Домой» слева). Здесь вам нужно сделать две вещи:

1- Измените фрейм previewLayer на:

self.previewLayer.frame=self.view.bounds;

Вам необходимо установить рамку слоя предварительного просмотра в пределах экрана, чтобы рамка слоя предварительного просмотра изменялась при повороте экрана (вы не можете использовать рамку корневого представления, потому что это не изменяется с поворотом, но границы корневого представления делать). В вашем примере вы устанавливаете фрейм previewlayer на свойство previewView, которое я не вижу.

2- Вам нужно повернуть соединение слоя предварительного просмотра с вращением устройства. Добавьте этот код в viewDidAppear:

-(void) viewDidAppear:(BOOL)animated
{
  [super viewDidAppear:YES];

  //Get Preview Layer connection
  AVCaptureConnection *previewLayerConnection=self.previewLayer.connection;

  if ([previewLayerConnection isVideoOrientationSupported])
    [previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; 
}

Надеюсь, это решит эту проблему.

Полное раскрытие информации: это упрощенная версия, так как вам все равно, альбомная ориентация справа или альбомная слева.

person Khaled Barazi    schedule 25.02.2013
comment
Спасибо, это работает. Я действительно заставил его работать с self.previewLayer.orientation = UIInterfaceOrientationLandscapeLeft, но меня это не устраивало, потому что оно устарело. Как я могу добавить поддержку как для левого, так и для правого пейзажа? - person soleil; 26.02.2013
comment
Однако для кода требуется версия iOS 6.0, поскольку self.previewLayer.connection; Поэтому используйте captureVideoPreviewLayer.orientation = UIInterfaceOrientationLandscapeLeft; - person alones; 26.07.2013
comment
statusBarOrientation устарел в iOS 9, поэтому его больше нецелесообразно использовать. Кроме того, он возвращает UIDeviceOrientation, который является типом, отличным от AVCaptureVideoOrientation, поэтому может быть неопределенным, что произойдет, если вы, например, находитесь в неопределенной ориентации устройства или поместите свое устройство на стол. - person algal; 14.11.2017
comment
Согласно предложению algal, в настоящее время мы можем проверить текущую ориентацию с помощью UIApplication.shared.statusBarOrientation, а затем установить ориентацию видео с помощью AVCaptureVideoOrientation. Он отображает один к одному. - person haxpor; 29.06.2018

ЛУЧШИЙ ОТВЕТ ДЛЯ SWIFT 3.0 И XCODE 8.0

private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {
    
    layer.videoOrientation = orientation
    
    previewLayer.frame = self.view.bounds
    
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    if let connection =  self.previewLayer?.connection  {
        
        let currentDevice: UIDevice = UIDevice.current
        
        let orientation: UIDeviceOrientation = currentDevice.orientation
        
        let previewLayerConnection : AVCaptureConnection = connection
        
        if previewLayerConnection.isVideoOrientationSupported {
            
            switch (orientation) {
            case .portrait: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
                                
            case .landscapeRight: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft)
                                
            case .landscapeLeft: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight)
                                
            case .portraitUpsideDown: updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown)
                                
            default: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
            
            }
        }
    }
}

ЛУЧШИЙ ОТВЕТ ДЛЯ SWIFT 2.2 И XCODE 7.3

private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {
    
    layer.videoOrientation = orientation

    previewLayer.frame = self.view.bounds

}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    if let connection =  self.previewLayer?.connection  {
        
        let currentDevice: UIDevice = UIDevice.currentDevice()
        
        let orientation: UIDeviceOrientation = currentDevice.orientation
        
        let previewLayerConnection : AVCaptureConnection = connection
        
        if (previewLayerConnection.supportsVideoOrientation) {
            
            switch (orientation) {
            case .Portrait: updatePreviewLayer(previewLayerConnection, orientation: .Portrait)
                                
            case .LandscapeRight: updatePreviewLayer(previewLayerConnection, orientation: .LandscapeLeft)
                                
            case .LandscapeLeft: updatePreviewLayer(previewLayerConnection, orientation: .LandscapeRight)
                                
            case .PortraitUpsideDown: updatePreviewLayer(previewLayerConnection, orientation: .PortraitUpsideDown)
                                
            default: updatePreviewLayer(previewLayerConnection, orientation: .Portrait)
            
            }
        }
    }
}
person Maselko    schedule 12.04.2016
comment
Это невероятно красиво. - person Fattie; 20.05.2016
comment
Вот отличный код ... drivecurrent.com/devops/ - person Fattie; 21.05.2016
comment
Этот ответ не сработает, если вы быстро переключитесь из одного ландшафтного режима в другой. В этом сценарии метод макета не вызывается. Я также тестировал viewWillLayout и получил тот же результат. - person CodeBender; 05.08.2016
comment
В переключателе два случая (LandscapeRight и LandscapeLeft) перевернуты, но он отлично работает. - person Fox5150; 07.08.2016
comment
Это решение отлично работало для меня до iOS 11 / Swift 4. Это исправило его для меня stackoverflow.com/a/34480478/945247 - person Leon; 01.10.2017
comment
Вам не нужно писать break внутри переключателя swift, как в C. - person Juguang; 17.10.2017
comment
@Maselko, я пытался использовать ваш код, но у меня все еще возникают проблемы. У меня есть вопрос, если у вас есть время взглянуть: stackoverflow.com/questions / 46913953 / - person user2363025; 25.10.2017
comment
Все эти break утверждения не нужны. Кейсы Swift switch не проходят по умолчанию, как в Objective-C. - person Rob; 31.10.2017
comment
это eee1337 - person iThompkins; 20.10.2019
comment
Здорово!! это отлично работает также для Swift4 и iOS 9. - person Vincenzo; 14.12.2019

Мы не можем использовать

[previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; 

потому что UIInterfaceOrientation != AVCaptureVideoOrientation

Но мы можем просто проверить значения ... и это сработает со следующим кодом.

-(void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    switch (orientation) {
        case UIInterfaceOrientationPortrait:
            [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
            break;
        case UIInterfaceOrientationLandscapeLeft:
            [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
            break;
        case UIInterfaceOrientationLandscapeRight:
            [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
            break;
    }
}
person Emmanuel Crombez    schedule 13.05.2015
comment
Добавьте значение по умолчанию: break; - person netshark1000; 07.06.2015

Кажется, что API несколько изменился. videoOrientation теперь является свойством в свойстве connection слоя предварительного просмотра. Кроме того, нет необходимости использовать переключатель. Ответ для Swift 3.0:

override func viewDidLayoutSubviews() {
    self.configureVideoOrientation()
}

private func configureVideoOrientation() {
    if let previewLayer = self.previewLayer,
        let connection = previewLayer.connection {
        let orientation = UIDevice.current.orientation

        if connection.isVideoOrientationSupported,
            let videoOrientation = AVCaptureVideoOrientation(rawValue: orientation.rawValue) {
            previewLayer.frame = self.view.bounds
            connection.videoOrientation = videoOrientation
        }
    }
}
person John Rogers    schedule 25.08.2017
comment
Это должен быть ответ. Спасибо! - person Nick Yap; 30.10.2017
comment
Действительно мило. Небольшое исправление; последняя строка должна быть connection.videoOrientation = videoOrientation (удалить previewLayer.) - person JKvr; 23.01.2018
comment
Должен быть ответ, работает. previewLayer.connection допускает значение NULL, он должен быть в последней строке: previewLayer.connection? .videoOrientation = videoOrientation - person Bradley; 19.04.2018

Для тех, кто испытывает трудности с полнофункциональным предварительным просмотром камеры. Вот производственный код. Конечно же недостаток - лаг при смене ориентации. Если у кого-то есть лучшее решение для преодоления этого, поделитесь

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self initCamera];
}

- (void)initCamera
{
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil];
    if (captureInput) {
        mSession = [[AVCaptureSession alloc] init];
        [mSession addInput:captureInput];
    }
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
    if ([mSession isRunning]) {
        [mSession stopRunning];
        [mCameraLayer removeFromSuperlayer];

        [self initCamera];

        [self startCamera];
    }
}

- (void)startCamera
{
    [mSession startRunning];
    Settings::getInstance()->setClearColor(Color(0, 0, 0, 0));
    mCameraLayer = [AVCaptureVideoPreviewLayer layerWithSession: mSession];
    [self updateCameraLayer];
    [mCameraView.layer addSublayer:mCameraLayer];
}

- (void)stopCamera
{
    [mSession stopRunning];
    [mCameraLayer removeFromSuperlayer];
    Settings::getInstance()->setDefClearColor();
}

- (void)toggleCamera
{
    mSession.isRunning ? [self stopCamera] : [self startCamera];
    [mGLKView setNeedsDisplay];
}

- (void)updateCameraLayer
{
    mCameraLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    mCameraLayer.frame = mCameraView.bounds;
    float x = mCameraView.frame.origin.x;
    float y = mCameraView.frame.origin.y;
    float w = mCameraView.frame.size.width;
    float h = mCameraView.frame.size.height;
    CATransform3D transform = CATransform3DIdentity;
    if (UIDeviceOrientationLandscapeLeft == [[UIDevice currentDevice] orientation]) {
        mCameraLayer.frame = CGRectMake(x, y, h, w);
        transform = CATransform3DTranslate(transform, (w - h) / 2, (h - w) / 2, 0);
        transform = CATransform3DRotate(transform, -M_PI/2, 0, 0, 1);
    } else if (UIDeviceOrientationLandscapeRight == [[UIDevice currentDevice] orientation]) {
        mCameraLayer.frame = CGRectMake(x, y, h, w);
        transform = CATransform3DTranslate(transform, (w - h) / 2, (h - w) / 2, 0);
        transform = CATransform3DRotate(transform, M_PI/2, 0, 0, 1);
    } else if (UIDeviceOrientationPortraitUpsideDown == [[UIDevice currentDevice] orientation]) {
        mCameraLayer.frame = mCameraView.bounds;
        transform = CATransform3DMakeRotation(M_PI, 0, 0, 1);
    } else {
        mCameraLayer.frame = mCameraView.bounds;
    }
    mCameraLayer.transform  = transform;
}

    enter code here
person Lance Mao    schedule 14.03.2014
comment
решение для решения, которое состоит в том, чтобы переопределить метод viewDidLayoutSubviews в вашем контроллере представления и соответственно обновить границы - person Matej; 09.06.2015
comment
У меня сработал вызов updateCameraLayer. +1 от меня;) - person Ajay Sharma; 28.07.2016
comment
Я вызываю updateCameraLayer в viewDidAppear. Потребовались незначительные изменения: float x = mCameraView.bounds.origin.x и первое mCameraLayer.frame = mCameraView.bounds; можно опустить. - person Frank Hintsch; 03.12.2016
comment
Как исправить отставание? Когда я меняю ориентацию, я получаю 2-секундную задержку на слое предварительного просмотра. - person bakalolo; 17.05.2017

Поскольку с использованием вышеуказанного решения появляется предупреждение об устаревании и преобразовании, а установка videoOrientation, похоже, не работает в iOS7, я поставил проверки ориентации в моем получателе для AVCaptureVideoPreviewLayer следующим образом:

- (AVCaptureVideoPreviewLayer *) previewLayer
{
    if(!_previewLayer)
    {
        _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession: self.captureSession];

    [_previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];

    _previewLayer.frame = self.view.bounds; // Assume you want the preview layer to fill the view.

    [_previewLayer setPosition:CGPointMake(0,0)];

    if (UIDeviceOrientationLandscapeLeft == [[UIDevice currentDevice] orientation]) {
        _previewLayer.transform = CATransform3DMakeRotation(-M_PI/2, 0, 0, 1);
    }
    else if (UIDeviceOrientationLandscapeRight == [[UIDevice currentDevice] orientation])
    {
        _previewLayer.transform = CATransform3DMakeRotation(M_PI/2, 0, 0, 1);
    }
}

return _previewLayer;
}
person David van Dugteren    schedule 17.12.2013

Работает с Swift 4, Xcode 9:

  override func viewWillTransition(to size: CGSize,
                                   with coordinator: UIViewControllerTransitionCoordinator)
  {
    super.viewWillTransition(to: size, with: coordinator)
    guard
    let conn = self.previewLayer?.connection,
      conn.isVideoOrientationSupported
      else { return }
    let deviceOrientation = UIDevice.current.orientation
    switch deviceOrientation {
    case .portrait: conn.videoOrientation = .portrait
    case .landscapeRight: conn.videoOrientation = .landscapeLeft
    case .landscapeLeft: conn.videoOrientation = .landscapeRight
    case .portraitUpsideDown: conn.videoOrientation = .portraitUpsideDown
    default: conn.videoOrientation = .portrait
    }
  }

Здесь следует отметить одну тонкость: UIDeviceOrientation.landscapeRight сочетается с AVCaptureVideoOrientation.landscapeLeft.

Другой случай ландшафта тоже не подходит. Это сделано умышленно и устраняет досадное несоответствие между UIKit и AVFoundation. Если вы сопоставите случаи, сопоставив имена, это не сработает, и ваше видео будет перевернуто в альбомной конфигурации.

person algal    schedule 14.11.2017
comment
Поправьте насчет несоответствия ориентаций UIKit и AVFoundation. Это меня сбивало с толку. В моем случае мне также пришлось изменить фрейм previewLayer, чтобы он соответствовал новому размеру, используя: previewLayer.position = CGPointMake (size.width / 2.0, size.height / 2.0) previewLayer.frame = CGRectMake (0, 0, size.width. , размер. высота) - person Chuck Krutsinger; 07.06.2018

Выбранный ответ, работающий для Swift 4.2 - Xcode 10.0 - iOS 12.0:

var videoPreviewLayer: AVCaptureVideoPreviewLayer?

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  if let previewLayerConnection =  self.videoPreviewLayer?.connection, previewLayerConnection.isVideoOrientationSupported {
    updatePreviewLayer(layer: previewLayerConnection, orientation: UIApplication.shared.statusBarOrientation.videoOrientation)
  }
}

private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {
  layer.videoOrientation = orientation
  videoPreviewLayer?.frame = self.view.bounds
}

Не забудьте сопоставить UIInterfaceOrientation с AVCaptureVideoOrientation

extension UIInterfaceOrientation {

  public var videoOrientation: AVCaptureVideoOrientation {
    switch self {
    case .portrait:
      return AVCaptureVideoOrientation.portrait
    case .landscapeRight:
      return AVCaptureVideoOrientation.landscapeRight
    case .landscapeLeft:
      return AVCaptureVideoOrientation.landscapeLeft
    case .portraitUpsideDown:
      return AVCaptureVideoOrientation.portraitUpsideDown
    default:
      return AVCaptureVideoOrientation.portrait
    }
  }

}
person juliancadi    schedule 09.08.2018

Необходимо правильное сопоставление UIDeviceOrientation в AVCaptureVideoOrientation.

Если ваше приложение поддерживает device rotation, также необходимо resizing preview.frame, и этот func должен вызываться из viewDidLayoutSubviews() и viewWillTransition().

private func configureVideoOrientation() {
    if let preview = self.previewLayer,
        let connection = preview.connection {
        let orientation = UIDevice.current.orientation

        if connection.isVideoOrientationSupported {
            var videoOrientation: AVCaptureVideoOrientation
            switch orientation {
            case .portrait:
                videoOrientation = .portrait
            case .portraitUpsideDown:
                videoOrientation = .portraitUpsideDown
            case .landscapeLeft:
                videoOrientation = .landscapeRight
            case .landscapeRight:
                videoOrientation = .landscapeLeft
            default:
                videoOrientation = .portrait
            }
            preview.frame = self.view.bounds
            connection.videoOrientation = videoOrientation
        }
    }
}
person Water    schedule 10.09.2018

Привет, ребята, спасибо за все ответы и отзывы по этому поводу, я столкнулся с этим, когда работаю над быстрым приложением. Вы можете заставить камеру вращаться вместе с вашим устройством, используя этот код:

override func shouldAutorotate() -> Bool {
        if your cameraController.previewLayer.connection != nil {
            var currentDevice: UIDevice = UIDevice.currentDevice()
            var orientation: UIDeviceOrientation = currentDevice.orientation

            var previewLayerConnection : AVCaptureConnection = your cameraController.previewLayer.connection

            if (previewLayerConnection.supportsVideoOrientation)
            {
                switch (orientation)
                {
                case .Portrait:
                    previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
                    break
                case .LandscapeRight:
                    previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft
                    break
                case .LandscapeLeft:
                    previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
                    break
                default:
                    previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
                    break
                }
            }

        }
        return true
    }

Надеюсь это поможет!

person Pablo Segura    schedule 10.09.2015
comment
как получилось перевернуть правое и левое? У меня все еще возникают проблемы с сохранением изображения из этого слоя предварительного просмотра с правильной ориентацией. Вопрос здесь, если у вас есть время: stackoverflow.com/questions/46913953/ - person user2363025; 25.10.2017
comment
Удалите эти break утверждения. Это Swift без провалов в switch операторах, как в Objective-C. - person Rob; 31.10.2017

Версия Swift 5

  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    updatePreview()
  }

  func updatePreview() {
    let orientation: AVCaptureVideoOrientation
    switch UIDevice.current.orientation {
      case .portrait:
        orientation = .portrait
      case .landscapeRight:
        orientation = .landscapeLeft
      case .landscapeLeft:
        orientation = .landscapeRight
      case .portraitUpsideDown:
        orientation = .portraitUpsideDown
      default:
        orientation = .portrait
    }
    if previewLayer?.connection?.isVideoOrientationSupported == true {
      previewLayer?.connection?.videoOrientation = orientation
    }
    previewLayer.frame = view.bounds
  }
person Steven    schedule 03.04.2019

Сначала нам нужно создать AVCaptureVideoPreviewLayer и:

  1. установите его videoGravity (как в моем случае я использую небольшое представление для вывода видео).
  2. установить рамку.

    [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [_videoPreviewLayer setFrame:_viewPreview.layer.bounds];
    
  3. установить ориентацию изначально

    if (_videoPreviewLayer.connection.supportsVideoOrientation) {
            _videoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
        }
  4. установите ориентацию для каждого случая с помощью простого переключателя

    -(AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:

    (UIInterfaceOrientation)orientation {
    
        switch (orientation) {
            case UIInterfaceOrientationPortrait:
                return AVCaptureVideoOrientationPortrait;
            case UIInterfaceOrientationPortraitUpsideDown:
                return AVCaptureVideoOrientationPortraitUpsideDown;
            case UIInterfaceOrientationLandscapeLeft:
                return AVCaptureVideoOrientationLandscapeLeft ;
            case UIInterfaceOrientationLandscapeRight:
                return AVCaptureVideoOrientationLandscapeRight;
            default:
                break;
        }
        NSLog(@"Warning - Didn't recognise interface orientation (%d)",orientation);
        return AVCaptureVideoOrientationPortrait;
    

    }

  5. Поскольку устройство поддерживает как landscapeLeft, так и landscapeRight, используйте делегат, вызываемый при вращении:
    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
    if (_videoPreviewLayer.connection.supportsVideoOrientation) {
        _videoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:toInterfaceOrientation];
     }
    }
    
person Maniac One    schedule 01.10.2015

Я тоже столкнулся с тем же. Это было сделано для исправления ориентации моей камеры.

override func shouldAutorotate() -> Bool {
        return false
    }

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
        return UIInterfaceOrientation.LandscapeLeft
    }

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.LandscapeLeft
    }

Починить камеру

let previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.avCaptureSession)    
previewLayer.frame = self.view.layer.frame
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
person Cloy    schedule 05.08.2017

Ответ Маселко почти сработал для меня, за исключением того, что если ориентация строки состояния переворачивается, то изображение с камеры отображается вверх ногами. Я решил эту проблему, повторно вызвав логику Маселко, когда строка состояния переворачивается.

Вот мое модифицированное решение Maselko (проверено на ios12 / swift4):

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    setCameraOrientation()
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    setCameraOrientation()
}

@objc func setCameraOrientation() {
    if let connection =  self.previewLayer?.connection  {
        let currentDevice: UIDevice = UIDevice.current
        let orientation: UIDeviceOrientation = currentDevice.orientation
        let previewLayerConnection : AVCaptureConnection = connection
        if previewLayerConnection.isVideoOrientationSupported {
            let o: AVCaptureVideoOrientation
            switch (orientation) {
            case .portrait: o = .portrait
            case .landscapeRight: o = .landscapeLeft
            case .landscapeLeft: o = .landscapeRight
            case .portraitUpsideDown: o = .portraitUpsideDown
            default: o = .portrait
            }

            previewLayerConnection.videoOrientation = o
            previewLayer!.frame = self.view.bounds
        }
    }
}
person sttawm    schedule 06.07.2018

Улучшенная версия ответа Маселко

Работает просто отлично!

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  if let connection =  previewView.connection  {
    if connection.isVideoOrientationSupported {
      let videoOrientation = AVCaptureVideoOrientation.init(rawValue: UIApplication.shared.statusBarOrientation.rawValue)!
      connection.videoOrientation = videoOrientation
      previewView.frame = self.view.bounds
    }
  }
}
person Kenan Karakecili    schedule 11.04.2019

Единственный способ, которым это сработало для меня в iOS 8-11.1 без каких-либо проблем, - это сделать это, и я должен упомянуть, что в моем случае я загружал свое приложение только в ландшафтном режиме, но оно должно работать во всех ориентациях (кстати, вы можете наложить камеру вручную через просмотр изображений или что-то еще, что вы хотите, очень легко)

@interface ViewController (){
    AVCaptureVideoPreviewLayer * previewLayer;
    AVCaptureSession* session;
}
@property (weak, nonatomic) IBOutlet UIView *cameraPreviewView;


-(void)viewDidLoad{
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil];
    if (captureInput) {
        session = [[AVCaptureSession alloc] init];
        [session addInput:captureInput];
    }
    previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
    [previewLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
    [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];

    CALayer *rootLayer = [self.cameraPreviewView layer];
    [rootLayer setMasksToBounds:YES];
    [previewLayer setFrame:[self.view bounds]];
    [rootLayer addSublayer:previewLayer];
    [session startRunning];

    //Orientation Code is in viewDidLayoutSubviews method
}
-(void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    switch (orientation) {
        case UIInterfaceOrientationPortrait:
            [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
            break;
        case UIInterfaceOrientationLandscapeLeft:
            [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
            break;
        case UIInterfaceOrientationLandscapeRight:
            [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
            break;
        default:break;
    }
}
person Reza.Ab    schedule 06.11.2017

Ответ @Maselko правильный, но с одной стороны: вы должны использовать UIApplication.shared.statusBarOrientation вместо UIDevice.current.orientation, потому что ориентация устройства - это то, как ваше устройство физически расположено. Он ломается, когда ваше устройство находится в альбомной ориентации, но ваш пользовательский интерфейс не поддерживает эту ориентацию (например, когда я делал приложение камеры только для альбомной ориентации и инициировал просмотр, когда устройство находится в портретном положении).

private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {

    layer.videoOrientation = orientation

    previewLayer.frame = self.view.bounds

}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let connection =  self.previewLayer?.connection  {

        let currentDevice: UIDevice = UIDevice.current

        let orientation = UIApplication.shared.statusBarOrientation

        let previewLayerConnection : AVCaptureConnection = connection

        if previewLayerConnection.isVideoOrientationSupported {

            switch (orientation) {
            case .portrait: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)

                break

            case .landscapeRight: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft)

                break

            case .landscapeLeft: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight)

                break

            case .portraitUpsideDown: updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown)

                break

            default: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)

                break
            }
        }
    }
}
person Teng L    schedule 28.06.2018

Вот решение, которое я использую в Swift 4.

Это коротко и прекрасно работает для меня.

open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    let videoLayer = self.previewLayer
    coordinator.animate(alongsideTransition: { (context: UIViewControllerTransitionCoordinatorContext) in

        guard let connection = videoLayer?.connection, connection.isVideoOrientationSupported, let orientation = AVCaptureVideoOrientation(rawValue: UIApplication.shared.statusBarOrientation.rawValue) else {
            return
        }

        connection.videoOrientation = orientation
        videoLayer?.frame = self.view.bounds

    }) { (context: UIViewControllerTransitionCoordinatorContext) in
        // handle any completion logic here...
    }
}
person digitalHound    schedule 09.08.2018

person    schedule
comment
когда я использую этот код с устройством в альбомном режиме, рамка слоя предварительного просмотра находится не в правильном положении, а появляется в нижнем левом углу. Не могли бы вы посоветовать, как мне это исправить? Благодарность - person DrPatience; 16.11.2015
comment
@DrPatience, вы можете преодолеть это, когда снова установите фрейм previewLayer: previewLayer? .Frame = CGRectMake (0, 0, size.width, size.height) - person Olivier de Jonge; 07.01.2016
comment
Осторожно - UIDevice.currentDevice().orientation возвращает ориентацию устройства независимо от того, поддерживает ли это пользовательский интерфейс. Лучше сравнить с UIApplication.shared.statusBarOrientation, чтобы убедиться, что ваш videoPreviewLayer имеет ту же ориентацию, что и ваш пользовательский интерфейс. - person Dylan Hand; 14.04.2017
comment
Все эти break утверждения не нужны. Кейсы Swift switch не проходят по умолчанию, как в Objective-C. Кроме того, previewLayerConnection.videoOrientation известен как AVCaptureVideoOrientation, поэтому вы можете упростить операторы до previewLayerConnection.videoOrientation = .portrait. - person Rob; 31.10.2017