SwiftUI Map mapType hybrid / satellite - possible?

SwiftUI Map() doesn't provide a way of changing mapType from .standard. Some older (1yr+) StackOverflow posts claim being able to set mapType by using a custom initialiser or an extension, but neither approach seems to work for my needs and set up (Swift 5, iOS 15 beta, Xcode 13).

When I use .onAppear() { MKMapView.appearance().mapType = mapStyle}, where mapStyle is a var of the View, the style is set (e.g. to .hybrid) successfully on the first invocation of the view (the map) but not subsequent ones, which are as .standard. I can't think of any way of solving this.

I could use MapKit with UIViewRepresentable, which I've done a few times before with other projects, but it'd be nice to use "pure" SwiftUI.

Any ideas?

Cheers, Michaela

PS - the reason for wanting satellite or hybrid is that I often need to return to a location in a forest area with poor, or non existent, trail mapping.

Yes it is possible to change the mapType from hybrid to satellite. The "trick" is to use an ObservableObject to change the state of the view. Here is a basic setup that works well for me, passing the MapModel through an EnvironmentObject. Let us know if this works for you.

import SwiftUI
import Foundation
import MapKit

@main
struct TestApp: App {
    var mapModel = MapModel()
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(mapModel)
        }
    }
}

class MapModel: ObservableObject {
    @Published var mapType = MKMapType.standard
    @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 35.685, longitude: 139.7514), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
}


struct ContentView: View {
    @EnvironmentObject var mapModel: MapModel

    @State private var mapType: Int = 0
    @State private var mapTypes = ["Standard", "Satellite", "Hybrid"]

    var body: some View {
        ZStack (alignment: .topLeading) {
            MapView().edgesIgnoringSafeArea(.all)
            mapTools
        }
    }

    var mapTools: some View {
        HStack {
            Spacer()
            Picker(selection: Binding<Int> (
                get: {self.mapType},
                set: {
                    self.mapType = $0
                    self.mapModel.mapType = self.getMapType()
                }
            ), label: Text("")) {
                ForEach(0 ..< mapTypes.count) {
                    Text(self.mapTypes[$0])
                }
            }.pickerStyle(SegmentedPickerStyle())
            .labelsHidden()
            .frame(width: 222, height: 60)
            .clipped()
            Spacer()
        }
    }

    func getMapType() -> MKMapType {
        switch mapType {
        case 0: return .standard
        case 1: return .satellite
        case 2: return .hybrid
        default:
            return .standard
        }
    }

}

struct MapView: UIViewRepresentable  {

    @EnvironmentObject var mapModel: MapModel

    let mapView = MKMapView()

    func makeUIView(context: Context) -> MKMapView {
        mapView.mapType = mapModel.mapType
        mapView.setRegion(mapModel.region, animated: true)
        return mapView
    }

    func updateUIView(_ uiView: MKMapView, context: Context) {
        uiView.mapType = mapModel.mapType
    }

}

I'm still dealing with exactly the same problem, so for now I have decided to use the UIViewRepresentable road. Let me know if something improves with the SwiftUI Map.

With iOS 17 beta you finally get the mapStyle functionality.

You can use like this: .mapStyle(.imagery(elevation: .realistic))

Watch the WWDC 2023 session for more insights:

https://developer.apple.com/videos/play/wwdc2023/10043/

SwiftUI Map mapType hybrid / satellite - possible?
 
 
Q