Galen Smith
Galen Smith

Reputation: 387

How to pass a user selectable MapStyle to a map

I have an app with UIKit maps that I would like to update to the new SwiftUI map logic released in iOS 17. My UIKit logic has a segmented picker allowing selection of three different map styles: standard, hybrid, and imagery. I am having trouble in iOS 17 passing the MapStyle from the segmented picker to the map view.

I am seeing the following two compile errors associated with the picker:

  1. Generic struct 'Picker' requires that 'MapStyle' conform to 'Hashable

  2. Instance method 'tag' requires that 'MapStyle' conform to 'Hashable'

I ran across online a suggestion to place picker options within an enum conforming to CaseIterable and Identifiable. Swift automatically makes the enum conform to Equatable and Hashable by doing this.

enum Styles: MapStyle, Identifiable {
    case standard
    case hybrid
    case imagery

    var id: Styles { self }
}

But this results in different errors: 'Styles' declares raw type 'MapStyle', but does not conform to RawRepresentable and conformance could not be synthesized.
Cannot convert value of type 'Styles' to expected argument type 'MapStyle'

So I might have a hashable parameter, but mapStyle() may not have it in a usable form.

Any ideas how to resolve this is much appreciated.

struct DetailView: View {

    var item: TravelEntries   // coreData

    @State private var mapStyle: MapStyle = .standard

    var coordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: item.entryLat,
            longitude: item.entryLong)
    }

    var body: some View {

            VStack {
                Map() {
                    Marker(item.entryCatName ?? "", coordinate: coordinate)
                        .tint(.red)
                }
                .mapStyle(mapStyle)

                Picker("", selection: $mapStyle) {
                    Text("Default").tag(MapStyle.standard)
                    Text("Transit").tag(MapStyle.hybrid)
                    Text("Satellite").tag(MapStyle.imagery)
                }
                .pickerStyle(SegmentedPickerStyle())

        }
    }
}

Upvotes: 1

Views: 790

Answers (1)

You could try using this simple approach, where you calculate the var selectedMapStyle: MapStyle based on the Picker integer selection.

struct ContentView: View {
    @State private var mapType: Int = 0
    
    var coordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(latitude: 35.685, longitude: 139.7514)
    }
    
    var selectedMapStyle: MapStyle {
        return switch(mapType) {
          case 0: .standard
          case 1: .hybrid
          case 2: .imagery
          default: .standard
        }
    }
    
    var body: some View {
        VStack {
            Map {
                Marker("item name", coordinate: coordinate)
                    .tint(.red)
            }
            .mapStyle(selectedMapStyle)
            
            Picker("", selection: $mapType) {
                Text("Default").tag(0)
                Text("Transit").tag(1)
                Text("Satellite").tag(2)
            }
            .pickerStyle(SegmentedPickerStyle())
        }
    }
}

Upvotes: 6

Related Questions