Using UIKit with UIViewRepresentable

Learn how to use UIViewRepresentable to wrap a UIKit view, like a custom map view, so it can be used seamlessly within your SwiftUI application.

Using UIKit with UIViewRepresentable

Note:

This tutorial explains how to bridge the gap between SwiftUI and UIKit by wrapping UIKit views in a SwiftUI-compatible container.

Overview

While you can add many different libraries and components directly in SwiftUI, some solutions may not work efficiently on older iOS versions. Furthermore, there are many powerful, battle-tested components available in UIKit that don't have a direct SwiftUI equivalent yet. To use these UIKit elements in a SwiftUI view, we use the UIViewRepresentable protocol.

To conform to UIViewRepresentable, you must implement two required functions in your struct:

  • makeUIView(context:): This function is responsible for creating and configuring the initial state of your UIKit view.
  • updateUIView(_:context:): This function is called whenever the state of your SwiftUI view changes, allowing you to update the wrapped UIKit view.

Reference:

Example: Wrapping a Custom MKMapView

A common use case for UIViewRepresentable is wrapping an MKMapView to get more control than the basic SwiftUI Map view provides, such as customizing annotations or overlays.

Let's create a MapView struct that wraps MKMapView.

import SwiftUI
import MapKit

// 1. Create a struct that conforms to UIViewRepresentable
struct MapView: UIViewRepresentable {

    // 2. Implement the makeUIView function
    func makeUIView(context: Context) -> MKMapView {
        // Create the initial MKMapView
        let mapView = MKMapView()
        // You can do initial setup here
        return mapView
    }

    // 3. Implement the updateUIView function
    func updateUIView(_ uiView: MKMapView, context: Context) {
        // This function is called when the view needs to be updated.
        // For a simple map, you might not need to add any code here.
        // For more complex views, you would update the uiView based on new data from SwiftUI.
        
        // Example: Center the map on a specific coordinate
        let coordinate = CLLocationCoordinate2D(
            latitude: 34.011_286, longitude: -116.166_868
        )
        let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        uiView.setRegion(region, animated: true)
    }
}

// You can then use this MapView in any SwiftUI view like this:
struct ContentView: View {
    var body: some View {
        MapView()
            .edgesIgnoringSafeArea(.all) // Make it take up the full screen
    }
}

Note:

UIViewRepresentable is an essential tool for any SwiftUI developer, as it provides a bridge to the vast ecosystem of UIKit components, ensuring you're never limited by what SwiftUI offers out of the box.