How to open the ImagePicker in SwiftUI?

后端 未结 8 1282
生来不讨喜
生来不讨喜 2020-11-29 23:53

I need to open the ImagePicker in my app using SwiftUI, how can I do that?

I thought about using the UIImagePickerController, but I don\'t know how to do that in Swi

8条回答
  •  北海茫月
    2020-11-30 00:48

    I implemented a version that I think is more general and extensible. I used a Subject instead of a Binding to solve the problem where it's undoable/inappropriate to add another Binding in your view.

    For example, you created a List showing a set of images stored in the underlying storage and you wanted to add an image with the image picker. In this case, it's very hard/ugly to have that image added to your underlying storage.

    So I used a subject to transfer the image and you can simply observe it and add the new images to some storage, or if you want it to behave just like a Binding, it's one line of code, too. (modifying your State in your observation)

    Then I wrapped the preferences into a ViewModel so it won't get cluttered if you want to have more subjects or configurations.

    import SwiftUI
    import Combine
    
    struct ImagePickerView : UIViewControllerRepresentable {
    
        @Binding var model: ImagePickerViewModel
    
        typealias UIViewControllerType = UIImagePickerController
    
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
    
        func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController {
    
            let controller = UIImagePickerController()
            controller.delegate = context.coordinator
            controller.allowsEditing = false
            controller.mediaTypes = ["public.image"]
            controller.sourceType = .photoLibrary
            return controller
    
        }
    
        func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext) {
            // run right after making
    
        }
    
        class Coordinator : NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
            var parentView: ImagePickerView
    
            init(_ parentView: ImagePickerView) {
                self.parentView = parentView
            }
    
            func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
                parentView.model.isPresented = false
            }
    
            func imagePickerController(_ picker: UIImagePickerController,
                                              didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
    
                guard let uiImage = info[.originalImage] as? UIImage else { return }
    
                let image = Image(uiImage: uiImage)
                parentView.model.pickedImagesSubject?.send([image])
                parentView.model.isPresented = false
    
            }
    
        }
    
    }
    
    struct ImagePickerViewModel {
        var isPresented: Bool = false
        let pickedImagesSubject: PassthroughSubject<[Image], Never>! = PassthroughSubject<[Image], Never>()
    }
    

    Usage:

    struct SomeView : View {
    
        @EnvironmentObject var storage: Storage
        @State var imagePickerViewModel = ImagePickerViewModel()
    
        var body: some View {
            Button(action: { self.imagePickerViewModel.isPresented.toggle() }) { ... }
                .sheet(isPresented: $imagePickerViewModel.isPresented) {
                    ImagePickerView(model: self.$imagePickerViewModel)
                }
                .onReceive(imagePickerViewModel.pickedImagesSubject) { (images: [Image]) -> Void in
                    withAnimation {
                        // modify your storage here
                        self.storage.images += images
                    }
                }
        }
    }
    

提交回复
热议问题