Search code examples
swiftuiimagepickercontrollerswiftui

ImagePicker in SwiftUI


I'm trying to create an Image Picker in SwiftUI following this tutorial. Basically, the idea is that when the user taps on a grey rectangle on the screen, an image picker comes up. When they select an image, it is imported into the grey rectangle.

The problem is that, when I run the code, I get the following error: Thread 1: Fatal error: Reading Environment<Binding<PresentationMode>> outside View.body

This appears on the line parent.presentationMode.wrappedValue.dismiss() in the Image Picker Struct.

My code looks like this:

Image Picker


struct ImagePicker: UIViewControllerRepresentable {
    
  @Environment(\.presentationMode) var presentationMode
  @Binding var image: UIImage?
    
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage
            }

        parent.presentationMode.wrappedValue.dismiss()
        }     
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
   
    }
}

Content View

import SwiftUI

struct ContentView: View {
    
    @State private var image: Image?
    @State private var showingImagePicker = false
    @State private var inputImage: UIImage?
    

    var body: some View {
     NavigationView {
             ZStack {
                 Rectangle()
                     .fill(Color.secondary)

                 if image != nil {
                     image?
                         .resizable()
                         .scaledToFit()
                 } else {
                     Text("Tap to select a picture")
                 }
             }
             .onTapGesture {
                 self.showingImagePicker = true
             }


        .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
            ImagePicker(image: self.$inputImage)
        }
     }
    }

    func loadImage() {
        guard let inputImage = inputImage else { return }
        image = Image(uiImage: inputImage)
    }
    
}

Similar to the solution recommended here, I tried replacing @Environment(\.presentationMode) var presentationMode with @Binding var isPresented: Bool in the Image Picker Struct and replacing the troublesome line parent.presentationMode.wrappedValue.dismiss() with parent.isPresented = false.

However, this caused a different problem - the image didn't load when the Sheet was dismissed (I guess because I wasn't calling the dismiss() method anywhere).

I'm pretty new to Swift and coding, so apologies if I could have been more succinct in my explanation anywhere and feel free to make corrections.


Solution

  • ok, looks like you are testing with 13.1 -> update to 13.2 - there it works