如何让SwiftUI中的AVFoundation预览视图忽略安全区域并铺满整个屏幕?
解决SwiftUI中AVFoundation相机预览铺满全屏(忽略安全区域)的问题
我来帮你搞定这个问题!你遇到的情况是因为几个细节没配合好,导致edgesIgnoringSafeArea没起作用,而且相机预览层的缩放模式也不对。咱们一步步来调整:
问题根源
PreviewHolder作为UIViewRepresentable组件,没有被强制设置为占满所有可用布局空间AVCaptureVideoPreviewLayer的videoGravity用了.resizeAspect,这个模式会保持画面宽高比,不会拉伸/裁剪来填满屏幕- 你之前添加
edgesIgnoringSafeArea的层级没有覆盖到核心预览视图
具体修改方案
1. 调整相机预览层的缩放模式
在PreviewView的didMoveToSuperview方法里,把videoGravity改成.resizeAspectFill,让画面铺满整个预览层:
override func didMoveToSuperview() { super.didMoveToSuperview() if nil != self.superview { self.videoPreviewLayer.session = self.captureSession // 替换为resizeAspectFill,实现全屏铺满 self.videoPreviewLayer.videoGravity = .resizeAspectFill self.captureSession?.startRunning() } else { self.captureSession?.stopRunning() } }
2. 确保PreviewHolder占满SwiftUI布局空间
修改PreviewHolder的makeUIView方法,给返回的PreviewView添加约束配置,让它完全填充父视图:
struct PreviewHolder: UIViewRepresentable { func makeUIView(context: UIViewRepresentableContext<PreviewHolder>) -> PreviewView { let previewView = PreviewView() // 禁用自动调整mask,让SwiftUI的布局约束生效 previewView.translatesAutoresizingMaskIntoConstraints = false return previewView } func updateUIView(_ uiView: PreviewView, context: UIViewRepresentableContext<PreviewHolder>) { } typealias UIViewType = PreviewView }
3. 在SwiftUI视图里正确应用忽略安全区域
简化DemoVideoStreaming的布局,直接给PreviewHolder添加edgesIgnoringSafeArea,同时确保它占满整个屏幕空间:
struct DemoVideoStreaming: View { var body: some View { PreviewHolder() .edgesIgnoringSafeArea(.all) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) } }
完整修改后的代码
import SwiftUI import UIKit import AVFoundation class PreviewView: UIView { private var captureSession: AVCaptureSession? init() { super.init(frame: .zero) var allowedAccess = false let blocker = DispatchGroup() blocker.enter() AVCaptureDevice.requestAccess(for: .video) { flag in allowedAccess = flag blocker.leave() } blocker.wait() if !allowedAccess { print("!!! NO ACCESS TO CAMERA") return } // setup session let session = AVCaptureSession() session.beginConfiguration() let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .unspecified) guard videoDevice != nil, let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!), session.canAddInput(videoDeviceInput) else { print("!!! NO CAMERA DETECTED") return } session.addInput(videoDeviceInput) session.commitConfiguration() self.captureSession = session } override class var layerClass: AnyClass { AVCaptureVideoPreviewLayer.self } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } var videoPreviewLayer: AVCaptureVideoPreviewLayer { return layer as! AVCaptureVideoPreviewLayer } override func didMoveToSuperview() { super.didMoveToSuperview() if nil != self.superview { self.videoPreviewLayer.session = self.captureSession // 修改为resizeAspectFill,实现全屏铺满 self.videoPreviewLayer.videoGravity = .resizeAspectFill self.captureSession?.startRunning() } else { self.captureSession?.stopRunning() } } } struct PreviewHolder: UIViewRepresentable { func makeUIView(context: UIViewRepresentableContext<PreviewHolder>) -> PreviewView { let previewView = PreviewView() // 禁用自动调整mask,让SwiftUI的布局约束生效 previewView.translatesAutoresizingMaskIntoConstraints = false return previewView } func updateUIView(_ uiView: PreviewView, context: UIViewRepresentableContext<PreviewHolder>) { } typealias UIViewType = PreviewView } struct DemoVideoStreaming: View { var body: some View { PreviewHolder() .edgesIgnoringSafeArea(.all) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) } }
这样修改后,相机预览就会完全铺满iPhone的整个屏幕,包括安全区域位置啦。
内容的提问来源于stack exchange,提问作者Duck
相关产品推荐
相关产品推荐

