Ландшафтная ориентация 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 в ландшафт
- Захват будет повернут (как если бы вы удерживали камеру на 90 градусов)
- Он не будет полностью охватывать установленные границы
Проблема здесь в том, что 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 не будут обновляться, если устройство поворачивается с портретного режима на портретный перевернутый режим (очевидно, по соображениям эффективности).