如何正确使用SwiftUI Photo Picker上传图片至Firebase?
问题
使用SwiftUI原生图片选择器,选择和加载图片功能正常,但上传至Firebase时,不知道在viewModel.uploadPost(caption: caption, image: <??>)的image参数中传入什么。尝试调用selectedImage,收到错误提示它不是UIImage类型,以为已经在ImagePicker中完成了数据转UIImage的操作,作为Swift新手,希望得到帮助。
UploadPostView
struct UploadPostView: View { @StateObject var imagePicker = ImagePicker() @State private var caption = "" @Environment(\.presentationMode) var mode @ObservedObject var viewModel = UploadPostViewModel() var body: some View { NavigationView { VStack { HStack(alignment: .top, spacing: 16) { if let user = AuthViewModel.shared.currentUser { KFImage(URL(string: user.profileImageUrl ?? "")) .resizable() .scaledToFill() .frame(width: 64, height: 64) .cornerRadius(10) } TextField("Enter your post here", text: $caption, axis: .vertical) .lineLimit(5...10) }.padding() Spacer() if let image = imagePicker.image { HStack { ZStack (alignment: .top) { image .resizable() .scaledToFill() .frame(width: 100, height: 100) .cornerRadius(4) Button { self.imagePicker.image = nil } label: { Image(systemName: "xmark") .frame(width: 24, height: 24) .foregroundColor(.white) .padding(4) .background(.black) .clipShape(Circle()) } .offset(x: 45, y: -15) } Spacer() } .padding() } HStack (spacing: 24) { Text("Add to your post") .foregroundColor(Color(.darkGray)) Spacer() PhotosPicker(selection: $imagePicker.imageSelection) { Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 24, height: 24) }.foregroundColor(Color(.darkGray)) Button { } label: { Image(systemName: "at") .resizable() .scaledToFit() .frame(width: 22, height: 22) }.foregroundColor(Color(.darkGray)) } .padding() } .onReceive(viewModel.$didUploadPost) { success in if success { mode.wrappedValue.dismiss() } } .navigationTitle("Create Post") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button { mode.wrappedValue.dismiss() } label: { Image(systemName: "xmark") .resizable() .scaledToFit() .frame(width: 18, height: 18) .foregroundColor(Color(.darkGray)) } } ToolbarItem(placement: .navigationBarTrailing) { Button { viewModel.uploadPost(caption: caption, image: image)) // 问题所在 } label: { Text("Post") .font(.system(size: 18)) .bold() } } } } } }
UploadPostViewModel
class UploadPostViewModel: ObservableObject { @Published var didUploadPost = false @Published var didDeletePost = false let service = PostService() func uploadPost(caption :String, image: UIImage?) { service.uploadPost(caption: caption, image: image) { success in if success { //dismiss screen self.didUploadPost = true } else { // show error message to user.. } } } }
ImagePicker
@MainActor class ImagePicker: ObservableObject { @Published var image: UIImage? @Published var imageSelection: PhotosPickerItem? { didSet { if let imageSelection { Task { try await loadTransferable(from: imageSelection) } } } } func loadTransferable(from imageSelection: PhotosPickerItem?) async throws { do { if let data = try await imageSelection?.loadTransferable(type: Data.self) { if let uiImage = UIImage(data: data) { self.image = uiImage } } } catch { print(error.localizedDescription) image = nil } } }
解决方案
核心问题是:Toolbar按钮里的image是if let image = imagePicker.image代码块内的局部变量,代码块外部无法访问。而ImagePicker类中已经通过@Published var image: UIImage?保存了转换后的UIImage对象,直接传入这个值即可。
修改Toolbar中Post按钮的代码:
Button { viewModel.uploadPost(caption: caption, image: imagePicker.image) } label: { Text("Post") .font(.system(size: 18)) .bold() }
额外建议:为避免用户未选图就上传,可添加判断逻辑:
Button { guard let selectedImage = imagePicker.image else { // 此处可添加提示用户选择图片的逻辑 return } viewModel.uploadPost(caption: caption, image: selectedImage) } label: { Text("Post") .font(.system(size: 18)) .bold() }
这样就能正确将选中的UIImage传给上传方法,匹配UploadPostViewModel中uploadPost方法的参数类型。
内容的提问来源于stack exchange,提问作者amelia
相关产品推荐
相关产品推荐

