< How to save images to the user’s photo library | Reading custom values from the environment with @EnvironmentObject > |
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.
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.