SwiftUI自定义相机切换Tab加载缓慢,如何优化性能?
解决Tab切换到自定义相机视图耗时过长的问题
问题根源
每次切换到Camera Tab时,PhotoView会被重新初始化,连带CameraModel也会重建,而AVCaptureSession的配置、权限检查和启动是耗时操作,重复执行就会导致延迟。另外当前拍照逻辑里会停止session,切换回来又要重新启动,进一步加剧了等待时间。
解决方案
1. 保持CameraModel的生命周期(核心优化)
把CameraModel实例提升到父视图ContentView中,避免每次切换Tab都销毁重建模型:
修改ContentView:
struct ContentView: View { @State private var selectedTab: Tab = .Home // 将CameraModel提升到父视图,维持全局生命周期 @StateObject private var cameraModel = CameraModel() var body: some View { VStack(spacing: 0) { switch selectedTab { case .Camera: // 传入已创建的相机模型 PhotoView(camera: cameraModel) .transition(.identity) case .Records: Color.gray .transition(.identity) case .Home: HomeView() .transition(.identity) case .Test: Color.cyan .transition(.identity) case .Settings: SettingsView() .transition(.identity) } CustomTabBar(selectedTab: $selectedTab) } } }
修改PhotoView,接收外部传入的模型:
struct PhotoView: View { @ObservedObject var camera: CameraModel var body: some View { ZStack { // 原有视图布局代码保持不变 GeometryReader { proxy in let size = proxy.size CameraPreview(camera: camera, size: size) } VStack { // 原有按钮逻辑保持不变 } } .onAppear { // 仅当session未运行时才启动,避免重复执行初始化 if !camera.session.isRunning { camera.Check() } } } }
2. 优化Session的启停逻辑
当前takePic方法里会延迟停止session,导致切换回来需要重新启动。修改逻辑,让session保持运行状态:
修改CameraModel的takePic方法:
func takePic() { DispatchQueue.global(qos: .background).async { self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self) DispatchQueue.main.async { withAnimation { self.isTaken.toggle() } } } // 移除这行代码,不要停止session // DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // self.session.stopRunning() // } }
修改reTake方法,无需重新启动session:
func reTake() { DispatchQueue.main.async { withAnimation { self.isTaken.toggle() } self.isSaved = false } // 移除session.startRunning(),因为session一直处于运行状态 }
3. 预加载相机资源(可选)
如果希望第一次切换到相机Tab时完全无延迟,可以在App启动时提前初始化相机:
在ContentView中添加:
struct ContentView: View { // ... 原有代码 var body: some View { VStack(spacing: 0) { // ... 原有代码 } .onAppear { // 提前检查权限并配置session cameraModel.Check() } } }
4. 优化预览层适配
修改CameraPreview,确保预览层能正确适配视图大小变化,同时避免重复启动session:
struct CameraPreview: UIViewRepresentable { @ObservedObject var camera: CameraModel var size: CGSize func makeUIView(context: Context) -> UIView { let view = UIView() camera.preview = AVCaptureVideoPreviewLayer(session: camera.session) camera.preview.videoGravity = .resizeAspectFill view.layer.addSublayer(camera.preview) // 仅在session未运行时启动 if !camera.session.isRunning { camera.session.startRunning() } return view } func updateUIView(_ uiView: UIViewType, context: Context) { // 更新预览层frame,适配视图尺寸变化 camera.preview.frame = uiView.bounds } }
优化效果
- 相机模型仅初始化一次,避免重复执行耗时的
AVCaptureSession配置 - session保持运行状态,切换Tab时无需重新启动
- 提前预加载资源,消除第一次切换的等待延迟
内容的提问来源于stack exchange,提问作者burak
相关产品推荐
相关产品推荐

