M6L8 : Image Picker

This lesson takes us through the somewhat convoluted method by which an app can select a photo from the user’s photo library or use the camera.

Apparently, this is long-winding and convoluted because it’s not yet something SwiftUI can do and so we use a lot of UIKit code.

This is the code of our ImagePicker:

import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    
    @Environment(\.presentationMode) var presentationMode
    var selectedSource: UIImagePickerController.SourceType
    @Binding var recipeImage: UIImage?
    
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let imagePickerController = UIImagePickerController()
        imagePickerController.delegate = context.coordinator
        imagePickerController.sourceType = selectedSource
        return imagePickerController
    }
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
        
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        var parent: ImagePicker
        init(parent: ImagePicker) {
            self.parent = parent
        }
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
                parent.recipeImage = image
            }
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}

I honestly can’t summarise what this does too much right now, only that it needs to be done. I shall be printing it and adding it to my Lever Arch file for frequent referral and revision.

In order to use our ImagePicker code, we need to add a few things to our view that calls up the code.

import SwiftUI

struct AddRecipeView: View {
    ...
    @State private var isShowingImagePicker = false
    @State private var selectedImageSource = UIImagePickerController.SourceType.photoLibrary
    @State private var placeHolderImage = Image("noImageAvailable")
    var body: some View {
        ...
        ScrollView (showsIndicators: false) {
                VStack {
                    placeHolderImage
                        .resizable()
                        .scaledToFit()
                    HStack {
                        Button("Photo Library") {
                            selectedImageSource = .photoLibrary
                            isShowingImagePicker = true
                        }
                        Text(" | ")
                        Button("Camera") {
                            selectedImageSource = .camera
                            isShowingImagePicker = true
                        }
                    }
                    .sheet(isPresented: $isShowingImagePicker, onDismiss: loadImage) {
                        ImagePicker(selectedSource: selectedImageSource, recipeImage: $recipeImage)
                    }
                    ...
                }
            }
        }
        .padding(.horizontal)
    }
    func loadImage() {
        if recipeImage != nil {
            placeHolderImage = Image(uiImage: recipeImage!)
        }        
    }
}

Well, it does work, although we do need to use an actual device in order to use the camera, but there’s also one addition to our Info.plist that we need:

Now, I’m going off to re-read all of this one…