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

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

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

Dependency Injection in iOS with SwiftUI