SwiftUI using UIImagePickerController produces nil UIImage

Hi,

I'm using UIImagePickerController in swiftUI through UIViewControllerRepresentable:

Code Block
import SwiftUI
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: Context /*UIViewControllerRepresentableContext<ImagePicker>*/) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context /*UIViewControllerRepresentableContext<ImagePicker>*/) {
    }
}


In my main view I have an optional UIImage to bind the selected image from the picker, which is displayed through a modal sheet

Code Block
@State private var inputImage: UIImage?
...
.sheet(item: $activeSheet, onDismiss: dismissSheet) { item in
            switch(item) {
            case .selectPic:
                ImagePicker(image: $inputImage)
            case .editPic:
                DetailedPicView(inputImage: inputImage)
            }
        }
...


When the
Code Block
activeSheet

variable is later changed to
Code Block
.editPic

the DetailedPicView view is displayed through the same modal sheet.

HOWEVER:

this DetailedPicView view always receives a nil image, even if I chose an existing image.

Please note that the onDismiss: dismissSheet function starts as follows:

Code Block
func dismissSheet() {
        guard let inputImage = inputImage else { return }
...
    }


and the guard is always passed!
The debugger even shows the correct picture right after this step (curiously enough, it doesn't if the breakpoint is exactly on the guard line).
Later when I'll call the subview... the debugger is no more able to show the pic and the unwrap will crash.
Needless to say, there is nothing else touching the input image var.
I also tried to immediately switch to the other subview by simplifying the onDismiss: dismissSheet function as follows:

Code Block
func dismissSheet() {
        guard let inputImage = inputImage else { return }
         activeSheet = .editPic // <--
}

activating the other sheet/view, but result is the same: nil image passed.

I'm using Xcode 12.4 (12D4e)

Any hint ?





Answered by OOPer in 669541022

Any hint ?

It is a known issue that @State variables do not work when used in the closure of sheet.
A possible workaround is making another View from the content of the sheet:
Code Block
struct YourView: View {
//...
var body: some View {
//...
.sheet(item: $activeSheet, onDismiss: dismissSheet) { item in
PicView(item: item, inputImage: $inputImage)
}
//...
}
//...
}
struct PicView: View {
var item: ...
@Binding var inputImage: UIImage?
var body: some View {
switch(item) {
case .selectPic:
ImagePicker(image: $inputImage)
case .editPic:
DetailedPicView(inputImage: inputImage)
}
}
}


Accepted Answer

Any hint ?

It is a known issue that @State variables do not work when used in the closure of sheet.
A possible workaround is making another View from the content of the sheet:
Code Block
struct YourView: View {
//...
var body: some View {
//...
.sheet(item: $activeSheet, onDismiss: dismissSheet) { item in
PicView(item: item, inputImage: $inputImage)
}
//...
}
//...
}
struct PicView: View {
var item: ...
@Binding var inputImage: UIImage?
var body: some View {
switch(item) {
case .selectPic:
ImagePicker(image: $inputImage)
case .editPic:
DetailedPicView(inputImage: inputImage)
}
}
}


Douh!
Thank you OOPer, your proposal works perfectly!

SwiftUI using UIImagePickerController produces nil UIImage
 
 
Q