iOS 17下Swift相机应用如何绕过竖屏锁定实现自动旋转?
在iOS 17中绕过系统竖屏锁定实现相机应用自由旋转
问题分析
iOS 16+版本中,系统全局竖屏锁定的优先级高于视图控制器的supportedInterfaceOrientations和shouldAutorotate设置,所以单纯重写这两个属性无法绕过锁定。要实现类似WhatsApp相机的效果,必须通过主动向系统请求界面几何更新的方式实现。
解决方案
通过UIWindowScene.requestGeometryUpdate(_:completionHandler:)方法主动请求界面方向变更,同时监听设备方向变化并适配相机预览层的方向。
1. 监听设备方向变化
在视图控制器的viewDidLoad中开启设备方向通知监听:
override func viewDidLoad() { super.viewDidLoad() // 开启设备方向通知 UIDevice.current.beginGeneratingDeviceOrientationNotifications() NotificationCenter.default.addObserver( self, selector: #selector(deviceOrientationChanged), name: UIDeviceOrientationDidChangeNotification, object: nil ) }
2. 主动请求界面方向更新
实现方向变化的处理方法,通过windowScene请求更新界面方向:
@objc private func deviceOrientationChanged() { guard let windowScene = view.window?.windowScene else { return } let deviceOrientation = UIDevice.current.orientation // 过滤无效方向(未知、屏幕朝上/朝下) guard let targetInterfaceOrientation = deviceOrientation.toInterfaceOrientation(), targetInterfaceOrientation != .unknown else { return } // 向系统请求更新界面方向 let orientationMask = UIInterfaceOrientationMask(rawValue: targetInterfaceOrientation.rawValue) windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientationMask)) { error in if let error = error { print("方向更新请求失败: \(error.localizedDescription)") } } // 同步更新相机预览层方向 updateCameraPreviewOrientation(for: targetInterfaceOrientation) } // 扩展:将UIDeviceOrientation转为UIInterfaceOrientation extension UIDeviceOrientation { func toInterfaceOrientation() -> UIInterfaceOrientation? { switch self { case .portrait: return .portrait case .portraitUpsideDown: return .portraitUpsideDown case .landscapeLeft: return .landscapeRight case .landscapeRight: return .landscapeLeft default: return nil } } }
3. 适配相机预览层方向
相机预览层AVCaptureVideoPreviewLayer不会自动跟随界面旋转,需要手动设置其视频方向:
// 假设你已经初始化了previewLayer(AVCaptureVideoPreviewLayer实例) private var previewLayer: AVCaptureVideoPreviewLayer? private func updateCameraPreviewOrientation(for orientation: UIInterfaceOrientation) { guard let previewLayer = previewLayer, let connection = previewLayer.connection, connection.isVideoOrientationSupported else { return } let videoOrientation: AVCaptureVideoOrientation switch orientation { case .portrait: videoOrientation = .portrait case .portraitUpsideDown: videoOrientation = .portraitUpsideDown case .landscapeLeft: videoOrientation = .landscapeLeft case .landscapeRight: videoOrientation = .landscapeRight default: videoOrientation = .portrait } connection.videoOrientation = videoOrientation }
4. 清理资源
在视图控制器销毁时移除监听,避免内存泄漏:
deinit { UIDevice.current.endGeneratingDeviceOrientationNotifications() NotificationCenter.default.removeObserver(self) }
注意事项
- 该方案仅适用于iOS 16及以上版本(你的环境iOS 17完全兼容)
- 无需额外权限,
requestGeometryUpdate是系统公开API - 测试时需确保相机预览层已正确关联到
AVCaptureSession
内容的提问来源于stack exchange,提问作者Ahmed rafaat
相关产品推荐
相关产品推荐

