You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让SwiftUI中的AVFoundation预览视图忽略安全区域并铺满整个屏幕?

解决SwiftUI中AVFoundation相机预览铺满全屏(忽略安全区域)的问题

我来帮你搞定这个问题!你遇到的情况是因为几个细节没配合好,导致edgesIgnoringSafeArea没起作用,而且相机预览层的缩放模式也不对。咱们一步步来调整:

问题根源

  1. PreviewHolder作为UIViewRepresentable组件,没有被强制设置为占满所有可用布局空间
  2. AVCaptureVideoPreviewLayer的videoGravity用了.resizeAspect,这个模式会保持画面宽高比,不会拉伸/裁剪来填满屏幕
  3. 你之前添加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 04:22:49