SwiftUI中如何在单个Sheet内使用照片选择器并展示选中照片?
优化方案:单Sheet内切换内容实现需求
直接在同一个Sheet中切换「照片选择器」和「选中图片展示」的内容,完全不需要切换Sheet实例、禁用动画或依赖定时器,同时解决崩溃问题。
核心思路
- 用状态变量控制Sheet内部的显示模式(选择器/图片预览)
- 移除多Sheet切换逻辑,全程保持单个Sheet处于打开状态
- 使用可选绑定避免强制解包导致的崩溃
- 利用SwiftUI的视图自动更新特性,无需手动管理动画
完整代码实现
import SwiftUI import PhotosUI struct PhotoPicker: UIViewControllerRepresentable { @Binding var selectedImage: UIImage? let onImageSelected: () -> Void // 图片选中后的回调,用于切换Sheet内的视图 func makeUIViewController(context: Context) -> PHPickerViewController { var configuration = PHPickerConfiguration() configuration.filter = .images configuration.selectionLimit = 1 let picker = PHPickerViewController(configuration: configuration) picker.delegate = context.coordinator return picker } func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, PHPickerViewControllerDelegate { var parent: PhotoPicker init(_ parent: PhotoPicker) { self.parent = parent } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { if results.isEmpty { picker.dismiss(animated: true) return } guard let result = results.first, result.itemProvider.canLoadObject(ofClass: UIImage.self) else { return } result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in guard let self = self else { return } if let error = error { print("加载图片失败:\(error.localizedDescription)") return } DispatchQueue.main.async { self.parent.selectedImage = image as? UIImage self.parent.onImageSelected() // 触发视图切换 picker.dismiss(animated: true) // 只关闭照片选择器,Sheet保持打开 } } } } } // 控制Sheet内部的显示状态 enum SheetContentMode { case photoPicker, imagePreview } struct PhotoPickerUIKitView: View { @State private var selectedImage: UIImage? @State private var isSheetPresented = false @State private var sheetContentMode: SheetContentMode = .photoPicker var body: some View { Button("选择照片") { sheetContentMode = .photoPicker isSheetPresented = true } .padding() .sheet(isPresented: $isSheetPresented) { // 同一个Sheet内切换内容 Group { switch sheetContentMode { case .photoPicker: PhotoPicker(selectedImage: $selectedImage) { sheetContentMode = .imagePreview // 选中图片后切换到预览模式 } .ignoresSafeArea() case .imagePreview: if let image = selectedImage { Image(uiImage: image) .resizable() .scaledToFit() .padding() } else { // 兜底视图,防止意外情况 Text("未选中图片") .font(.title) .foregroundColor(.gray) } } } } } }
关键改进点说明
- 移除多Sheet切换逻辑:使用
isSheetPresented控制单个Sheet的显示,用sheetContentMode控制Sheet内部的视图切换,全程不需要关闭再打开Sheet,视觉过渡完全流畅。 - 解决崩溃问题:移除了原代码中
selectedImage!的强制解包,改用if let image = selectedImage的可选绑定,不再依赖空的.onChange(of: selectedImage)回调。 - 无需手动管理动画:只让照片选择器自身关闭,Sheet始终保持打开状态,系统动画正常工作,完全删除了定时器和
UIView.setAnimationsEnabled的手动控制代码。 - 更清晰的回调逻辑:通过
onImageSelected回调通知父视图切换Sheet内的显示模式,代码逻辑更直观,耦合度更低。
内容的提问来源于stack exchange,提问作者Mastelotto
相关产品推荐
相关产品推荐

