【发布时间】:2014-02-11 01:28:25
【问题描述】:
如何让“AVCaptureVideoPreviewLayer”以横向正确显示?它在纵向上可以正常工作,但不会旋转,并且当父视图控制器处于横向时会显示旋转的相机捕获。
【问题讨论】:
标签: ios avfoundation calayer landscape
如何让“AVCaptureVideoPreviewLayer”以横向正确显示?它在纵向上可以正常工作,但不会旋转,并且当父视图控制器处于横向时会显示旋转的相机捕获。
【问题讨论】:
标签: ios avfoundation calayer landscape
仅仅改变 AVCaptureVideoPreviewLayer 的连接方向是不够的。 这只会更新您在屏幕上看到的图像。
但实际图像仍将保持不变。您可以在不同方向打印输出图像大小并检查结果。
要使旋转真正起作用,您还需要更改 AVCaptureSession 连接的方向。
func updateVideoOrientation() {
if let previewLayerConnection = previewLayer.connection, previewLayerConnection.isVideoOrientationSupported {
previewLayerConnection.videoOrientation = currentVideoOrientation
}
if let captureSessionConnection = captureSession.connections.first, captureSessionConnection.isVideoOrientationSupported {
captureSessionConnection.videoOrientation = currentVideoOrientation
}
}
【讨论】:
Jacksanford 和 NeonEye 的回答很棒。不过,我有一个快速的建议。
因为 statusBarOrientation 在 iOS 13 中已弃用,所以我最终使用了
connection.videoOrientation = self.interfaceOrientation(toVideoOrientation: UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? .portrait
【讨论】:
Swift 5 (iOS 13.0+):
func updateVideoOrientation() {
guard let videoPreviewLayer = self.videoPreviewLayer else {
return
}
guard videoPreviewLayer.connection!.isVideoOrientationSupported else {
print("isVideoOrientationSupported is false")
return
}
let statusBarOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
let videoOrientation: AVCaptureVideoOrientation = statusBarOrientation?.videoOrientation ?? .portrait
videoPreviewLayer.frame = view.layer.bounds
videoPreviewLayer.connection?.videoOrientation = videoOrientation
videoPreviewLayer.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 转换
【讨论】:
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
}
}
}
【讨论】:
除了 viewWillLayoutSubviews(),你还必须实现 didRotateFromInterfaceOrientation()。
这是因为如果设备从纵向模式旋转到纵向倒置模式,子视图布局将不会更新(显然是出于效率原因)。
【讨论】:
首先,答案
- (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 设备旋转到横向时会遇到两个潜在问题
这里的问题是“CALayer”不支持自动旋转,因此,与您添加为子视图的“UIView”不同,它不会在其父“UIView”旋转时旋转。因此,每次父视图的边界发生变化时,您都必须手动更新其框架(不是父视图的框架,因为框架在旋转后保持不变)。这是通过覆盖容器视图控制器中的“viewWillLayoutSubviews”来实现的。
其次,您应该使用“videoOrientation”属性通知 AVFoundation 方向,以便它正确进行预览。
希望这会有所帮助。
【讨论】: