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

如何在State变化时调用函数?Firebase存储图片更新问题

解决State变化时自动更新Firebase Storage图片的问题

问题现状

当前通过onAppear调用persistImageToStorage()上传图片,首次选择图片时正常,但更换图片后,因onAppear仅在视图首次出现时触发,导致上传函数不再执行;尝试监听image的onChange未生效。

解决方案

核心思路是监听image状态的变化,而非依赖仅触发一次的onAppear,提供两种可行实现方式:

方式一:在父视图中监听image变化

直接在父视图的顶层添加onChange(of: image)修饰器,一旦image有新值就触发上传操作。

修改后的父视图代码:

@State var shouldShowImagePicker: Bool = false
@State var image: UIImage?

var body: some View {
    Button(action: {
        shouldShowImagePicker.toggle()
    }) {
        if let image = self.image {
            Image(uiImage: image)
                .resizable()
                .frame(maxWidth: 300, maxHeight: 300)
                .scaledToFit()
        } else {
            Text("Select Image")
                .frame(width: 300, height: 300)
        }
    }
    .fullScreenCover(isPresented: $shouldShowImagePicker, onDismiss: nil) {
        ImagePicker(image: $image)
    }
    // 监听image状态变化,触发上传
    .onChange(of: image) { newImage in
        guard newImage != nil else { return }
        persistImageToStorage()
    }
}

private func persistImageToStorage() {
    let ref = FirebaseManager.shared.storage.reference(withPath: "image")
    guard let imageData = self.image?.jpegData(compressionQuality: 0.5) 
          else { return }
    ref.putData(imageData, metadata: nil) { metadata, err in
        if let err = err {
            print(err)
            return
        }
        ref.downloadURL { url, err in
            if let err = err {
                print(err)
                return
            }
            // 可选:处理下载URL,例如保存到Firestore或本地
            if let url = url {
                print("图片上传完成,下载地址:\(url)")
            }
        }
    }
}

方式二:在ImagePicker中添加选中回调

给ImagePicker增加图片选中后的闭包回调,直接在选图完成时触发上传。

修改后的ImagePicker代码:

import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var image: UIImage?
    // 添加图片选中后的回调闭包
    var onImageSelected: (UIImage) -> Void

    private let controller = UIImagePickerController()

    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }

    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        let parent: ImagePicker

        init(parent: ImagePicker) {
            self.parent = parent
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let selectedImage = info[.originalImage] as? UIImage {
                parent.image = selectedImage
                // 触发上传回调
                parent.onImageSelected(selectedImage)
            }
            picker.dismiss(animated: true)
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            picker.dismiss(animated: true)
        }
    }

    func makeUIViewController(context: Context) -> some UIViewController {
        controller.delegate = context.coordinator
        return controller
    }

    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}

父视图中使用修改后的ImagePicker:

.fullScreenCover(isPresented: $shouldShowImagePicker, onDismiss: nil) {
    ImagePicker(image: $image) { _ in
        self.persistImageToStorage()
    }
}

注意事项

  • 若需避免重复上传相同图片,可在上传前对比当前图片与上次上传的图片数据,或添加状态标记控制上传时机。
  • 若之前onChange未生效,大概率是将onChange添加到了Image子视图上,而非父视图的顶层区域,导致状态变化时未触发监听。

内容的提问来源于stack exchange,提问作者AdamLeet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 08:12:27