Search code examples
swiftuimapboxmapbox-ios

Mapbox + SwiftUI: UI Refresh not propagating


I have integrated Mapbox with SwiftUI using the following example:

https://github.com/mapbox/mapbox-maps-swiftui-demo

It works fine. However when trying to display other @State variables on the View Stack, the UI Refresh propagation stops going down to the Mapbox call updateUIView()

For example, you can replicate the problem by replacing ContentView.swift from the above repository with the following code:

import SwiftUI
import Mapbox

struct ContentView: View {

    @State var annotations: [MGLPointAnnotation] = [
        MGLPointAnnotation(title: "Mapbox", coordinate: .init(latitude: 37.791434, longitude: -122.396267))
    ]

    var body: some View {
        ZStack {
            VStack {
                MapView(annotations: $annotations).centerCoordinate(.init(latitude: 37.791293, longitude: -122.396324)).zoomLevel(16)
                Button(action: {
                    let rand = Float.random(in: 37.79...37.80)
                    self.annotations.append(MGLPointAnnotation(title: "Mapbox", coordinate: .init(latitude: CLLocationDegrees(rand), longitude: -122.396261)))
                }) {
                    Text("Button")
                    Text("\(self.annotations.count)")
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Running the above code indicates that the Text("\(self.annotations.count)") UI gets updated - however, the annotations are not refreshed (hence updateUIView() is not called).

If I comment // Text("\(self.annotations.count)") then annotations are refreshed (and updateUIView() is called)

Does anybody have any ideas of what might be the issue? Or am I missing something here?

Thanks!


Solution

  • Answering my own question here thanks to this post

    https://github.com/mapbox/mapbox-maps-swiftui-demo/issues/3#issuecomment-623905509

    In order for this to work it is necessary to update the UIView being rendered inside Mapview:

    func updateUIView(_ uiView: MGLMapView, context: Context) {
        updateAnnotations(uiView)
        trackUser()
    }
    
    private func updateAnnotations(_ view: MGLMapView) {
        if let currentAnnotations = view.annotations {
            view.removeAnnotations(currentAnnotations)
        }
        view.addAnnotations(annotations)
    }