iosswiftuimapbox

How to add zoom buttons to Mapbox SwiftUI view?


I'm developing a SwiftUI app with Mapbox. How can I add zoom buttons to my map? My current code that works in the .camera viewport state. But how can I apply zoom when the viewport is in the .idle state? Is there a common solution for any state? Please note that this question relates only to the Mapbox for SwiftUI iOS SDK, not Apple MapKit or any other map SDK.

    .overlay(alignment: .trailing) {
        Button {
            guard
                let cam = viewport.camera,
                let zoom = cam.zoom
            else { return }
            // Compute new zoom level (clamped)
            let newZoom = min(zoom + 1, 20)
            // Animate the viewport change
            withViewportAnimation {
                viewport = .camera(
                    center: cam.center,
                    zoom: newZoom,
                    bearing: cam.bearing,
                    pitch: cam.pitch
                )
            }
        } label: {
            Image(systemName: "plus")
        }
        .buttonStyle(MapFloatingButtonStyle())
     }

Solution

  • You are using the wrong technique to get the current zoom level of the map. Viewport.camera.zoom doesn't hold the current zoom level, it holds the last value you tried to set when changing viewport to .camera(). So, your code will work when the map first loads, but if the user interacts with the map, viewport.isIdle becomes true (and viewport.camera no longer exists), and the next button press will return early due to your guard.

    What you want to do is get ahold of the MapboxMap instance, where you can read MapboxMap.cameraState.zoom. This is the actual current zoom of the map's camera.

    Button(action: {
        let zoom = self.map?.cameraState.zoom ?? 0
        let newZoom = min(zoom + 1, 20)
        withViewportAnimation {
            viewport = .camera(zoom: newZoom)
        }
        
    })
    

    In this case, self.map exists because I set it in Map.onMapLoaded() .

    MapReader { proxy in
        Map(viewport: $viewport)
            .mapStyle(.streets)
            .onMapLoaded { _ in
                self.map = proxy.map
            }
    }