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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:26:16