Ландшафтная ориентация AVCaptureVideoPreviewLayer

Как я могу получить "AVCaptureVideoPreviewLayer" для правильного отображения в альбомной ориентации? Он отлично работает в портрете, но не вращается и показывает поворот камеры, когда контроллер родительского вида находится в альбомной ориентации.

4 ответов


во-первых, ответ

- (void)viewWillLayoutSubviews {
  _captureVideoPreviewLayer.frame = self.view.bounds;
  if (_captureVideoPreviewLayer.connection.supportsVideoOrientation) {
    _captureVideoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation];
  }
}

- (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;
}

я наткнулся на несколько сообщений SO по этому вопросу и не мог найти простого объяснения, поэтому я подумал, что поделюсь своим собственным.

Если вы последуете примеру Apple по этому вопросу, вы столкнетесь с двумя потенциальными проблемами при повороте устройства iOS в landscape

  1. захват появится повернутым (как будто вы держите камеру на 90 градусов)
  2. он не будет охватывать установленные границы полностью

проблема здесь в том, что "CALayer" не поддерживает авторотацию, поэтому, в отличие от "UIView", который вы добавляете в качестве подвида, он не будет вращаться, когда его Родительский "UIView" вращается. Поэтому вы должны вручную обновлять его кадр каждый раз, когда границы родительского представления изменяются (не кадр родительского вида, так как кадр остается неизменным после вращения). Это достигается путем переопределения "viewWillLayoutSubviews" в контроллере представления контейнера.

во-вторых, вы следует использовать свойство videoOrientation для информирования AVFoundation об ориентации, чтобы он правильно выполнял предварительный просмотр.

надеюсь, что это помогает.


Swift3

func updateVideoOrientation() {
    guard let previewLayer = self.previewLayer else {
        return
    }
    guard previewLayer.connection.isVideoOrientationSupported else {
        print("isVideoOrientationSupported is false")
        return
    }

    let statusBarOrientation = UIApplication.shared.statusBarOrientation
    let videoOrientation: AVCaptureVideoOrientation = statusBarOrientation.videoOrientation ?? .portrait

    if previewLayer.connection.videoOrientation == videoOrientation {
        print("no change to videoOrientation")
        return
    }

    previewLayer.frame = cameraView.bounds
    previewLayer.connection.videoOrientation = videoOrientation
    previewLayer.removeAllAnimations()
}

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

    coordinator.animate(alongsideTransition: nil, completion: { [weak self] (context) in
        DispatchQueue.main.async(execute: {
            self?.updateVideoOrientation()
        })
    })
}

extension UIInterfaceOrientation {
    var videoOrientation: AVCaptureVideoOrientation? {
        switch self {
        case .portraitUpsideDown: return .portraitUpsideDown
        case .landscapeRight: return .landscapeRight
        case .landscapeLeft: return .landscapeLeft
        case .portrait: return .portrait
        default: return nil
        }
    }
}

override func viewWillLayoutSubviews() {
    self.previewLayer.frame = self.view.bounds
    if previewLayer.connection.isVideoOrientationSupported {
        self.previewLayer.connection.videoOrientation = self.interfaceOrientation(toVideoOrientation: UIApplication.shared.statusBarOrientation)
    }
}

func interfaceOrientation(toVideoOrientation orientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation {
    switch orientation {
    case .portrait:
        return .portrait
    case .portraitUpsideDown:
        return .portraitUpsideDown
    case .landscapeLeft:
        return .landscapeLeft
    case .landscapeRight:
        return .landscapeRight
    default:
        break
    }

    print("Warning - Didn't recognise interface orientation (\(orientation))")
    return .portrait
}

/ / SWIFT 3 ПРЕОБРАЗОВАНИЕ


помимо viewWillLayoutSubviews (), вам также необходимо реализовать didRotateFromInterfaceOrientation ().

Это потому, что макеты подвида не будут обновлены, если устройство вращается из портретного режима в портретный перевернутый режим (очевидно, по соображениям эффективности).