SwiftUI is an amazing framework to build apps but at the moment it is far from complete. For things which are not ported with SwiftUI, you have to write code to communicate with UIKit to add advanced functionality. In this post you will see how to wrap UIKit views and view controllers inside SwiftUI View.
SwiftUI is an amazing framework to build apps but at the moment it is far from complete. For things which are not ported with SwiftUI, you have to write code to communicate with UIKit to add advanced functionality.
In this post let’s see how to import an image from a photo library. UIKit comes with code for doing this. We need to write code to bridge UIKit and SwiftUI view.
Create a new swiftUI file and name it as ImagePicker.swift file and declare type as UIViewControllerRepresentable.
import SwiftUI
import UIKit
struct ImagePicker: UIViewControllerRepresentable {
}
Conforming to UIViewControllerRepresentable does require us to implement two methods: one called makeUIViewController(), which is responsible for creating the initial view controller, and another called updateUIViewController(), which is designed to let us update the view controller when some SwiftUI state changes.
With makeUIViewController() create a UIImagePickerController with the desired configurations. SwiftUI calls this method makeUIViewController when it is ready to display and then manages the view controller’s life cycle.
func makeUIViewController(context: Context) -> UIPageViewController {
let picker = UIImagePickerController()
return picker
}
With updateUIViewController() add set data for view controller. Right now we don’t have any data for the image picker controller.
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
We have created a ImagePicker struct which SwiftUI automatically calls makeUIViewController method which sends back ImagePickerController but our code doesn’t respond to any events inside ImagePicker. Now we have to define a Coordinator type that SwiftUI manages as part of representable view context.
Okay how do we do this?
Create a nested Coordinator class inside ImagePicker. SwiftUI manages your UIViewControllerRepresentable type’s Coordinator, and provides it as part of the context when calling the methods you created above.
struct ImagePicker: UIViewControllerRepresentable {
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
}
}
Because our coordinator class conforms to the UIImagePickerControllerDelegate protocol, we can make it the delegate of the UIKit image picker by modifying makeUIViewController().
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}
Add another method to ImagePicker to make the coordinator. SwiftUI calls this makeCoordinator() method before makeUIViewController(), so that you have access to the Coordinator object when configuring your view controller.
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
To make ImagePicker useful we have to use the functionality given by Coordinator. The UIImagePickerViewController looks for two methods, right now we are going to use didFinishPickingMediaWithInfo method. This is called when a user picks an image and gives us the information about the selected image.
Add two variables with type UIImage to store the UIImage data from picker and Environment binded property for presentation mode for controlling the ImagePicker presentation.
@Environment(\.presentationMode) var presentationMode
@Binding var image: UIImage?
Inside the Coordinator class add the didFinishPickingMediaWithInfo method.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let uiImage = info[.originalImage] as? UIImage {
parent.image = uiImage
}
parent.presentationMode.wrappedValue.dismiss()
}
That’s all in ImagePicker.swift. Let’s move on to the SwiftUI view where we have to implement image picker.
First let’s create a property inputImage which we have to link the property with ImagePicker. Then let’s create boolean @state which toggles ImagePicker view.
So in our SwiftUI view,
@State private var inputImage: UIImage?
@State private var showingImagePicker = false
Then we need a method that will be called when the ImagePicker view has been dismissed. Where this will place the selected image directly into the UI.
func loadImage() {
guard let inputImage = inputImage else { return }
image = Image(uiImage: inputImage)
}
Now add ImagePicker to sheet modifier with showImagePicker as toggle and loadImage function in sheet onDismiss parameter.
.sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
ImagePicker(image: self.$inputImage)
}
By configuring showImagePicker toggle wherever you want in the SwiftUI view you can pick an image and store it in inputImage Property. This completes the steps required for wrapping UIKit view controllers inside SwiftUI view.
From this post we saw how UIViewRepresentable helps in bridging UIKit and SwiftUI view and We worked with Coordinator to update the SwiftUI view from the UIKit and saw the lifecycle of events. Will meet you all soon with another important SwiftUI concept.
Up next
Robotic Process Automation in Insurance Industry