如何正确使用RotationCoordinator及其videoRotationAngleForHorizonLevelPreview设置相机预览方向
如何正确使用RotationCoordinator及其videoRotationAngleForHorizonLevelPreview设置相机预览方向
我明白你现在在SwiftUI里用新的RotationCoordinator替代废弃的AVCaptureConnection.videoOrientation时遇到了方向不对的问题,我之前也踩过这个坑,给你梳理下正确的用法:
首先得明确,RotationCoordinator是和你的AVCaptureDevice绑定的,它的videoRotationAngleForHorizonLevelPreview()方法会返回一个让预览画面保持水平的旋转角度,我们需要把这个角度应用到AVCaptureVideoPreviewLayer的变换上,而不是依赖旧的属性。
第一步:确保RotationCoordinator和捕获设备正确绑定
在初始化相机设备的时候,直接从当前设备创建RotationCoordinator,不要随便传一个无关的实例:
// 示例:相机初始化时创建rotationCoordinator guard let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { fatalError("无法获取后置相机") } let rotationCoordinator = captureDevice.rotationCoordinator
第二步:修改CameraPreviewView,添加旋转更新逻辑
原来的CameraPreviewView只做了初始化和帧更新,缺少了旋转角度的应用和方向变化监听。下面是修正后的完整代码:
struct CameraPreview: UIViewRepresentable { let previewLayer: AVCaptureVideoPreviewLayer let rotationCoordinator: AVCaptureDevice.RotationCoordinator func makeUIView(context: Context) -> CameraPreviewView { let view = CameraPreviewView(previewLayer: previewLayer, rotationCoordinator: rotationCoordinator) // 初始化时就设置一次旋转 view.updatePreviewRotation() return view } func updateUIView(_ uiView: CameraPreviewView, context: Context) { uiView.updatePreviewLayerFrame() // 尺寸变化后同步更新旋转 uiView.updatePreviewRotation() } } class CameraPreviewView: UIView { private let previewLayer: AVCaptureVideoPreviewLayer private let rotationCoordinator: AVCaptureDevice.RotationCoordinator private var orientationObserver: NSObjectProtocol? init(previewLayer: AVCaptureVideoPreviewLayer, rotationCoordinator: AVCaptureDevice.RotationCoordinator) { self.previewLayer = previewLayer self.rotationCoordinator = rotationCoordinator super.init(frame: .zero) setupPreviewLayer() setupOrientationObserver() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupPreviewLayer() { previewLayer.frame = bounds previewLayer.videoGravity = .resizeAspectFill layer.addSublayer(previewLayer) } private func setupOrientationObserver() { // 监听设备方向变化,实时更新预览旋转 orientationObserver = NotificationCenter.default.addObserver( forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main ) { [weak self] _ in self?.updatePreviewRotation() } // 开启设备方向监听 UIDevice.current.beginGeneratingDeviceOrientationNotifications() } func updatePreviewLayerFrame() { previewLayer.frame = bounds } func updatePreviewRotation() { // 获取让预览保持水平的旋转角度 let rotationAngle = rotationCoordinator.videoRotationAngleForHorizonLevelPreview() // 将角度应用到预览层的变换上 previewLayer.setAffineTransform(CGAffineTransform(rotationAngle: rotationAngle)) } deinit { // 移除监听,避免内存泄漏 if let observer = orientationObserver { NotificationCenter.default.removeObserver(observer) } UIDevice.current.endGeneratingDeviceOrientationNotifications() } }
关键注意点:
- 一定要把
videoRotationAngleForHorizonLevelPreview()返回的角度通过CGAffineTransform应用到previewLayer的transform上,这是新API的核心用法,替代了旧的videoOrientation设置。 - 必须监听
UIDevice.orientationDidChangeNotification,因为设备方向变化时,rotationCoordinator返回的角度会改变,需要实时更新预览层的变换。 - 在
updateUIView里也要调用updatePreviewRotation(),因为SwiftUI的视图尺寸变化时,预览层的帧更新后可能需要重新调整旋转。
这样调整后,你的相机预览应该就能正确跟随设备方向,保持水平显示了。
备注:内容来源于stack exchange,提问作者user924
相关产品推荐
相关产品推荐

