BLACK FRIDAY SALE: Save big on all my Swift books and bundles! >>

Importing an image into SwiftUI using PHPickerViewController

Paul Hudson    @twostraws   

In order to bring this project to life, we need to let the user select a photo from their library, then display it in ContentView. I’ve already shown you how this all works, so if you followed the introductory chapters you’ll already have most of the code you need.

If you missed those chapters, it’s not too late: create a new Swift file called ImagePicker.swift, then replace its code with this:

import PhotosUI
import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var image: UIImage?

    func makeUIViewController(context: Context) -> PHPickerViewController {
        var config = PHPickerConfiguration()
        config.filter = .images
        let picker = PHPickerViewController(configuration: config)
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {

    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, PHPickerViewControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }

        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            picker.dismiss(animated: true)

            guard let provider = results.first?.itemProvider else { return }

            if provider.canLoadObject(ofClass: UIImage.self) {
                provider.loadObject(ofClass: UIImage.self) { image, _ in
                    self.parent.image = image as? UIImage
                }
            }
        }
    }
}

That’s all code we’ve looked at before, so I’m not going to re-explain what it all does. Instead, I want to head back to ContentView.swift so we can make use of it.

First we need an @State Boolean to track whether our image picker is being shown or not, so start by adding this to ContentView:

@State private var showingImagePicker = false

Second, we need to set that Boolean to true when the big gray rectangle is tapped, so replace the // select an image comment with this:

showingImagePicker = true

Third, we need a property that will store the image the user selected. We gave the ImagePicker struct an @Binding property attached to a UIImage, which means when we create the image picker we need to pass in a UIImage for it to link to. When the @Binding property changes, the external value changes as well, which lets us read the value.

So, add this property to ContentView:

@State private var inputImage: UIImage?

Fourth, we need a method that will be called when the ImagePicker view has been dismissed. For now this will just place the selected image directly into the UI, so please add this method to ContentView now:

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

We can then call that whenever our inputImage value changes, by attaching an onChange() modifier somewhere in ContentView – it really doesn’t matter where, but after navigationTitle() would seem sensible.

.onChange(of: inputImage) { _ in loadImage() }

And finally, we need to add a sheet() modifier somewhere in ContentView. This will use showingImagePicker as its condition, and present an ImagePicker bound to inputImage as its contents.

So, add this directly below the existing navigationTitle() modifier:

.sheet(isPresented: $showingImagePicker) {
    ImagePicker(image: $inputImage)
}

That completes all the steps required to wrap a UIKit view controller for use inside SwiftUI. We went over it a little faster this time but hopefully it still all made sense!

Go ahead and run the app again, and you should be able to tap the gray rectangle to import a picture, and when you’ve found one it will appear inside our UI.

Tip: The ImagePicker view we just made is completely reusable – you can put that Swift file to one side and use it on other projects easily. If you think about it, all the complexity of wrapping the view is contained inside ImagePicker.swift, which means if you do choose to use it elsewhere it’s just a matter of showing a sheet and binding an image.

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.6/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.