Integrating SwiftUI with UIKit: When & How to Use UIViewControllerRepresentable
SwiftUI is a powerful framework for building modern iOS apps, but sometimes, we need to integrate existing UIKit components into SwiftUI. This is where UIViewControllerRepresentable
comes into play. It allows us to wrap a UIKit view controller and use it seamlessly in SwiftUI.
In this guide, we will explore:
✅ When to use UIViewControllerRepresentable
✅ Basic implementation
✅ Passing data between SwiftUI and UIKit
✅ Handling UIKit delegate methods in SwiftUI
✅ Real-world example: Integrating UIImagePickerController
๐น When to Use UIViewControllerRepresentable
You should use UIViewControllerRepresentable
when:
- You need to use a UIKit view controller inside SwiftUI.
- You want to integrate features not yet available in SwiftUI (e.g.,
UIImagePickerController
,AVPlayerViewController
). - You have existing UIKit code that you don’t want to rewrite in SwiftUI.
๐น Basic Implementation of UIViewControllerRepresentable
Let’s start with a simple example of embedding a UIKit UIViewController
in SwiftUI.
Example: Wrapping a Simple UIKit ViewController
import SwiftUI
import UIKit
struct MyUIKitViewController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let viewController = UIViewController()
viewController.view.backgroundColor = .blue
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// Updates if needed
}
}
struct ContentView: View {
var body: some View {
MyUIKitViewController()
.edgesIgnoringSafeArea(.all)
}
}
๐น How it works:
makeUIViewController(context:)
creates and returns the UIKit view controller.updateUIViewController(_:context:)
is used for updating the view when SwiftUI’s state changes.
๐น Passing Data Between SwiftUI and UIKit
If you need to pass data or receive updates from UIKit, you can use a Coordinator
.
Example: Passing Data to a UIKit ViewController
struct CustomViewController: UIViewControllerRepresentable {
var text: String
func makeUIViewController(context: Context) -> UILabelViewController {
let viewController = UILabelViewController()
viewController.labelText = text
return viewController
}
func updateUIViewController(_ uiViewController: UILabelViewController, context: Context) {
uiViewController.labelText = text
}
}
class UILabelViewController: UIViewController {
var labelText: String = "" {
didSet {
label.text = labelText
}
}
private let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
label.frame = view.bounds
label.textAlignment = .center
label.text = labelText
view.addSubview(label)
}
}
๐น How it works:
- The
UILabelViewController
updates the label text dynamically based on SwiftUI state changes.
๐น Handling UIKit Delegate Methods in SwiftUI
Some UIKit components require delegate methods (e.g., UIImagePickerController
). We use a Coordinator
for this.
Example: Using UIImagePickerController in SwiftUI
import SwiftUI
import UIKit
struct ImagePicker: UIViewControllerRepresentable {
@Binding var selectedImage: UIImage?
@Environment(\.presentationMode) var presentationMode
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
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[.originalImage] as? UIImage {
parent.selectedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}
Using ImagePicker in SwiftUI
struct ContentView: View {
@State private var image: UIImage?
@State private var isShowingPicker = false
var body: some View {
VStack {
if let image = image {
Image(uiImage: image)
.resizable()
.scaledToFit()
}
Button("Pick an Image") {
isShowingPicker = true
}
.sheet(isPresented: $isShowingPicker) {
ImagePicker(selectedImage: $image)
}
}
}
}
๐น How it works:
ImagePicker
usesUIImagePickerController
wrapped insideUIViewControllerRepresentable
.- A
Coordinator
is created to handle delegate callbacks. - The selected image is passed back to SwiftUI via a binding.
๐ฏ Final Thoughts
✅ Use UIViewControllerRepresentable
when integrating UIKit components into SwiftUI.
✅ Use Coordinator
to handle delegate methods and state updates.
✅ You can pass data between SwiftUI and UIKit seamlessly.
By combining SwiftUI’s declarative UI with UIKit’s mature components, you get the best of both worlds! ๐
Have you integrated UIKit with SwiftUI in your projects? Share your thoughts below! ๐
Comments
Post a Comment