Как перенести выбор изображения в другое представление?

У меня есть вид с камеры, где пользователи могут либо сделать фотографию с помощью своей камеры (которая открывается вместе с представлением), либо щелкнуть значок библиотеки фотографий и выбрать изображение из своей библиотеки. Если они сделают фотографию прямо сейчас, я смогу связатьcapturedImageв UploadPostView, но я не могу понять, как сделать то же самое, если они выберут фотографию из своей библиотеки. Я создаю что-то похожее на истории Instagram или Snapchat, где после того, как вы сделаете/выберете фотографию, вы сможете отредактировать ее (UploadPostView) перед публикацией.

Привязка переменной таким же образом, как и для captureImage, не работает. Я не очень хорошо знаком, но решил, что это произошло потому, что я привязывал настоящий класс ImagePicker. но когда я пытаюсь связать ImagePicker.image или ImagePicker.imageSelection... это тоже ничего не делает. Спасибо!!

CustomCameraView (когда пользователь делает фотографию или выбирает ее из своей библиотеки)

      import SwiftUI
import PhotosUI

struct CustomCameraView: View {
    let cameraService = CameraService()
    @Environment(\.dismiss) private var dismiss
    @StateObject var imagePicker = ImagePicker()
    @Binding var capturedImage: UIImage?

    var body: some View {
        
        //if photo taken
        if (imagePicker.image != nil) || (capturedImage != nil) {
            UploadPostView(capturedImage: $capturedImage)
        }
        
        //if photo not taken yet
        else {
            ZStack (alignment: .topLeading) {
                
                CameraView(cameraService: cameraService) { result in
                    switch result {
                    case .success(let photo):
                        if let data = photo.fileDataRepresentation() {
                            capturedImage = UIImage(data: data)
                        } else {
                            print("Error: no image data found")
                        }
                    case .failure(let err):
                        print(err.localizedDescription)
                    }
                }
                VStack (alignment: .leading) {
                    Button {
                        dismiss()
                    } label: {
                        Image("xmark")
                            .renderingMode(.template)
                            .resizable()
                            .frame(width: 28, height: 28)
                            .foregroundColor(.white)
                    }
                    
                    .padding()
                    Spacer()
                    
                    HStack {
                        PhotosPicker(selection: $imagePicker.imageSelection) {
                            Image("image-square")
                                .renderingMode(.template)
                                .resizable()
                                .frame(width: 32, height: 28)
                                .foregroundColor(.white)
                        }
                        Spacer()
                        Button {
                            cameraService.capturePhoto()
                        } label: {
                            Image(systemName: "circle")
                                .font(.system(size: 72))
                                .foregroundColor(.white)
                        }
                        Spacer()
                        Rectangle()
                            .foregroundColor(.clear)
                            .frame(width: 32, height: 28)
                    }
                    .padding()
                }
            }
            .cornerRadius(6)
            .background(.black)
        }
    }
}

Выбор изображения

      import SwiftUI
import PhotosUI

@MainActor
class ImagePicker: ObservableObject {
    
    @Published var image: Image?
    @Published var uiImage: 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.uiImage = uiImage
                    self.image = Image(uiImage: uiImage)
                }
            }
        } catch {
            print(error.localizedDescription)
            image = nil
        }
    }
}

UploadPostView (где пользователи могут редактировать свои фотографии перед загрузкой)

      import SwiftUI
import Kingfisher
import PhotosUI

struct UploadPostView: View {
    @Environment(\.dismiss) private var dismiss
    @ObservedObject var viewModel = UploadPostViewModel()
    @State var caption = ""
    @State var rating = 0
    @StateObject var imagePicker = ImagePicker()
    @Binding var capturedImage: UIImage?
    
    var body: some View {
            VStack {
                    if let image = imagePicker.image {
                        image
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
                            .cornerRadius(6)
                            .clipped()
                    }
                    else {
                        if let image = capturedImage {
                            Image(uiImage: image)
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
                                .cornerRadius(6)
                                .clipped()
                        }
                    }
                    
            
                HStack {
                    Spacer()
                    Button {
                        if let uiimage = imagePicker.uiImage {
                            viewModel.uploadPost(caption: caption, image: uiimage, rating: rating)
                            viewModel.loading = true
                        } else if let uiimage = capturedImage {
                            viewModel.uploadPost(caption: caption, image: uiimage, rating: rating)
                            viewModel.loading = true
                        }
                    } label: {
                        if viewModel.loading {
                            ProgressView()
                                .progressViewStyle(CircularProgressViewStyle(tint: .white))
                                .frame(width: 24, height: 24)
                                .padding()
                                .background(Color.accentColor)
                                .foregroundColor(.white)
                                .clipShape(Circle())
                        } else {
                            Image("send-fill")
                                .renderingMode(.template)
                                .resizable()
                                .frame(width: 24, height: 24)
                                .padding()
                                .background(Color.accentColor)
                                .foregroundColor(.white)
                                .clipShape(Circle())
                        }
                    }
                }.padding(8)
            }
        .background(.black)
        .onReceive(viewModel.$didUploadPost) { success in
            if success {
                dismiss()
            }
        }
        
    }
}

1 ответ

С вашим кодом есть 3 проблемы.

  1. У вас есть несколько источников истины. Например, несколько экземпляров и наличие нескольких переменных для изображения (,uiImageиimage)

Каждый раз, когда ты звонишьImagePicker()вы создаете другой экземпляр, и один не знает о другом.

  1. Множественные источники истины приводят ко второй проблеме — слишком большому количеству условных обозначений.

Есть много способов решить эту проблему, но я предпочитаю, когда это возможно, автономные модули многократного использования.

      PhotosPicker { result in
    switch result {
    case .success(let image):
        capturedImage = image
    case .failure(let error):
        print(error)//Provides a better description of an error.
        capturedImage = nil
    }
}

Этого можно добиться, поместив весь код Picker в отдельный файл .

      import PhotosUI
struct PhotosPicker: View{
    @State private var imageSelection: PhotosPickerItem?
    //
    let action: (Result<UIImage, Error>) async -> Void
    var body: some View{
        PhotosUI.PhotosPicker(selection: $imageSelection, matching: .images) {
            Image(systemName: "photo")
                .renderingMode(.template)
                .resizable()
                .frame(width: 32, height: 28)
                .foregroundColor(.white)
        }.task(id: imageSelection) {//Will trigger when there is a change to imageSelection
            if let _ = imageSelection{
                do{
                    let image = try await loadTransferable()
                    await action(.success(image))
                }catch{
                    await action(.failure(error))
                }
            }
        }
    }
    func loadTransferable() async throws -> UIImage {
        guard let data = try await imageSelection?.loadTransferable(type: Data.self) else{
            throw PickerError.unableGetData
        }
        //Make sure you don't overlook `else` when dealing with conditionals. The user should always be informed
        guard let uiImage = UIImage(data: data) else {
            throw PickerError.unableToCreateUIImage
        }
        return uiImage
    }
    enum PickerError: LocalizedError{
        case unableGetData
        case unableToCreateUIImage
    }
}

Используя описанный выше подход, вы устраняете необходимость вImagePickerи начать работать напрямую с .

Теперь это приводит к третьей проблеме. Ваш код сейчас небольшой, и он свеж в вашей памяти, но если вам придется вернуться к этому коду через несколько месяцев, вы, вероятно, столкнетесь с хрупкими областями. Например, условные предложения, которые не проходят или, другими словами, вы упускаете из видуelse. Первый был вloadTransferableфункция, а следующая находится в .

Предполагая, что ваш план состоит в том, чтобы никогда не показыватьUploadPostViewесли нетcapturedImageты можешь улучшить свойnilпроверьте, чтобы исключить необязательное в этом

      struct CustomCameraView: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var capturedImage: UIImage?
    
    var body: some View {
        //Unwrap Binding, to get rid of the optional below
        if let b = Binding($capturedImage) {
            UploadPostView(capturedImage: b)
        }else {
            ZStack (alignment: .topLeading) {
                
                VStack (alignment: .leading) {
                    Button {
                        dismiss()
                    } label: {
                        Image(systemName: "xmark")
                            .renderingMode(.template)
                            .resizable()
                            .frame(width: 28, height: 28)
                            .foregroundColor(.white)
                    }
                    
                    .padding()
                    Spacer()
                    
                    HStack {
                        PhotosPicker { result in
                            switch result {
                            case .success(let image):
                                capturedImage = image
                            case .failure(let error):
                                print(error)
                                capturedImage = nil
                            }
                        }
                        Spacer()
                        Button {
                            // cameraService.capturePhoto()
                        } label: {
                            Image(systemName: "circle")
                                .font(.system(size: 72))
                                .foregroundColor(.white)
                        }
                        Spacer()
                        Rectangle()
                            .foregroundColor(.clear)
                            .frame(width: 32, height: 28)
                    }
                    .padding()
                }
            }
            .cornerRadius(6)
            .background(.black)
        }
    }
}

Это очиститViewсущественно.

      struct UploadPostView: View {
    @Environment(\.dismiss) private var dismiss
    
    @Binding var capturedImage: UIImage //Optional removed
    
    var body: some View {
        VStack {
            
            Image(uiImage: capturedImage)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
                .cornerRadius(6)
                .clipped()
            
        }
        .background(.black)
    }
}
Другие вопросы по тегам