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., UIImagePickerControllerAVPlayerViewController).
  • 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 uses UIImagePickerController wrapped inside UIViewControllerRepresentable.
  • 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

Popular posts from this blog

Dependency Injection in iOS with SwiftUI

Using Core ML with SwiftUI: Build an AI-Powered App

CI/CD for iOS Projects with Xcode: A Complete Guide