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

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

Ответы

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

Ответ 3

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
}

//СВИФТ 3 КОНВЕРСИЯ

Ответ 4

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

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