使用选择器将 mapType 更改为 .satellite 等

Change the mapType to .satellite etc with a picker

我希望能够在 xCode 中将 mapType 从 .standard 更改为 .satellite 和 .hybrid 13.3 谁能告诉我是否可以使用此代码?我实施了一个选择器来完成这项工作,但不幸的是我无法让它工作。我成功地用不同的代码改变了它,但是按钮 map + 和 map - 将不再起作用

import Foundation
import SwiftUI
import MapKit

struct QuakeDetail: View {
    var quake: Quake
    
    @State private var region : MKCoordinateRegion
    init(quake : Quake) {
        self.quake = quake
        _region = State(wrappedValue: MKCoordinateRegion(center: quake.coordinate,
                                                         span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)))
    }
    
    @State private var mapType: MKMapType = .standard
    
    var body: some View {
        
        VStack {
                Map(coordinateRegion: $region, annotationItems: [quake]) { item in
                    MapMarker(coordinate: item.coordinate, tint: .red)
                } .ignoresSafeArea()
                HStack {
                    Button {
                        region.span.latitudeDelta *= 0.5
                        region.span.longitudeDelta *= 0.5
                    } label: {
                        HStack {
                            Text("map")
                            Image(systemName: "plus")
                        }
                    }.padding(5)//.border(Color.blue, width: 1)
                    Spacer()
                    QuakeMagnitude(quake: quake)
                    Spacer()
                    Button {
                        region.span.latitudeDelta /= 0.5
                        region.span.longitudeDelta /= 0.5
                    } label: {
                        HStack {
                            Text("map")
                            Image(systemName: "minus")
                        }
                    }                    
                }.padding(.horizontal)
                Text(quake.place)
                    .font(.headline)
                    .bold()
                Text("\(quake.time.formatted())")
                    .foregroundStyle(Color.secondary)
                Text("\(quake.latitude)   \(quake.longitude)")
            VStack {
                           Picker("", selection: $mapType) {
                                Text("Standard").tag(MKMapType.standard)
                                Text("Satellite").tag(MKMapType.satellite)
                                Text("Hybrid").tag(MKMapType.hybrid)
        
                            }
                            .pickerStyle(SegmentedPickerStyle())
                            .font(.largeTitle)
                        }
        }
    }
}

这是更改 mapType 但按钮不再起作用的代码:

import Foundation
import SwiftUI
import MapKit

struct QuakeDetail: View {
    var quake: Quake
    
    @State private var region : MKCoordinateRegion
    init(quake : Quake) {
        self.quake = quake
        _region = State(wrappedValue: MKCoordinateRegion(center: quake.coordinate,
                                                         span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)))
    }
    @State private var mapType: MKMapType = .standard
    
    var body: some View {
        
        VStack {
            MapViewUIKit(region: region, mapType: mapType)
                .edgesIgnoringSafeArea(.all)
            HStack {
                Button {
                    region.span.latitudeDelta *= 0.5
                    region.span.longitudeDelta *= 0.5
                } label: {
                    HStack {
                        Text("map")
                        Image(systemName: "plus")
                    }
                }.padding(5)//.border(Color.blue, width: 1)
                Spacer()
                QuakeMagnitude(quake: quake)
                Spacer()
                Button {
                    region.span.latitudeDelta /= 0.5
                    region.span.longitudeDelta /= 0.5
                } label: {
                    HStack {
                        Text("map")
                        Image(systemName: "minus")
                    }
                }
            }.padding(.horizontal)
            Text(quake.place)
                .font(.headline)
                .bold()
            Text("\(quake.time.formatted())")
                .foregroundStyle(Color.secondary)
            Text("\(quake.latitude)   \(quake.longitude)")
            Picker("", selection: $mapType) {
                Text("Standard").tag(MKMapType.standard)
                Text("Satellite").tag(MKMapType.satellite)
                Text("Hybrid").tag(MKMapType.hybrid)
                //Text("Hybrid flyover").tag(MKMapType.hybridFlyover)
            }
            .pickerStyle(SegmentedPickerStyle())
            .font(.largeTitle)
        }
    }
}

struct MapViewUIKit: UIViewRepresentable {
    
    let region: MKCoordinateRegion
    let mapType : MKMapType
    
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.setRegion(region, animated: false)
        mapView.mapType = mapType
        
        return mapView
    }
        
    func updateUIView(_ mapView: MKMapView, context: Context) {
        mapView.mapType = mapType
    }
}

我实现了代码,现在按钮可以正常工作,并且 mapType 可以正确更改,也非常感谢您对文档的指示。不幸的是,注释没有在地震位置显示大头针。我将标题从 London 更改为 quake.place,将坐标更改为 coordinate: CLLocationCoordinate2D(latitude: region.span.latitudeDelta, longitude: region.span.longitudeDelta) 但没有任何区别。这是我的更改:

import SwiftUI
import MapKit

struct QuakeDetail: View {
    var quake: Quake
    
    @State private var region : MKCoordinateRegion
    init(quake : Quake) {
        self.quake = quake
        _region = State(wrappedValue: MKCoordinateRegion(center: quake.coordinate,
                                                         span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)))
    }
    @State private var mapType: MKMapType = .standard
    
    var body: some View {
        
        VStack {

            MapViewUIKit(
                region: $region,
                mapType: mapType,
                annotation: Annotation(
                    title: quake.place,
                    coordinate: CLLocationCoordinate2D(latitude: region.span.latitudeDelta, longitude: region.span.longitudeDelta)
                ) // annotation
            ).ignoresSafeArea() // MapViewUIKit
            Spacer()
            HStack {
                Button {
                    region.span.latitudeDelta *= 0.5
                    region.span.longitudeDelta *= 0.5
                } label: {
                    HStack {
                        Image(systemName: "plus")
                    }
                }//.padding(5)
                Spacer()
                QuakeMagnitude(quake: quake)
                Spacer()
                Button {
                    region.span.latitudeDelta /= 0.5
                    region.span.longitudeDelta /= 0.5
                } label: {
                    HStack {
                        Image(systemName: "minus")
                    }
                }
            }.padding(.horizontal) // HStack + - buttons and quake magnitude
            
            Text(quake.place)
            Text("\(quake.time.formatted())")
                .foregroundStyle(Color.secondary)
            Text("\(quake.latitude)   \(quake.longitude)")
                .padding(.bottom, -5)
            Picker("", selection: $mapType) {
                Text("Standard").tag(MKMapType.standard)
                Text("Satellite").tag(MKMapType.satellite)
                Text("Hybrid").tag(MKMapType.hybrid)
            }
            .pickerStyle(SegmentedPickerStyle())
            
        }

    }
}

struct Annotation {
    let pointAnnotation: MKPointAnnotation

    init(title: String, coordinate: CLLocationCoordinate2D) {
        pointAnnotation = MKPointAnnotation()
        pointAnnotation.title = title
        pointAnnotation.coordinate = coordinate
    }
}

struct MapViewUIKit: UIViewRepresentable {

    @Binding var region: MKCoordinateRegion
    let mapType : MKMapType
    let annotation: Annotation

    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.setRegion(region, animated: false)
        mapView.mapType = mapType

        // Set the delegate so that we can listen for changes and
        // act appropriately
        mapView.delegate = context.coordinator

        // Add the annotation to the map
        mapView.addAnnotation(annotation.pointAnnotation)
        return mapView
    }

    func updateUIView(_ mapView: MKMapView, context: Context) {
        mapView.mapType = mapType
        // Update your region so that it is now your new region
        mapView.setRegion(region, animated: true)
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapViewUIKit

        init(_ parent: MapViewUIKit) {
            self.parent = parent
        }

        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            // We should handle dequeue of annotation view's properly so we have to write this boiler plate.
            // This basically dequeues an MKAnnotationView if it exists, otherwise it creates a new
            // MKAnnotationView from our annotation.
            guard annotation is MKPointAnnotation else { return nil }

            let identifier = "Annotation"
            guard let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) else {
                let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
                annotationView.canShowCallout = true
                return annotationView
            }

            annotationView.annotation = annotation
            return annotationView
        }

        func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
            // We need to update the region when the user changes it
            // otherwise when we zoom the mapview will return to its original region
            DispatchQueue.main.async {
                self.parent.region = mapView.region
            }
        }
    }
}

所以要使用 MKMapView 我们需要正确设置 UIViewRepresentableMKMapView 有一个委托,因此我们需要为我们的 mapView 设置委托。我们通过向我们的 UIViewRepresentable

添加一个协调器来做到这一点

所以这是一个完整的工作示例,它可能不是 100% 完美,但它展示了您可以做什么的总体思路。

我创建了自己的 ContentView,因为您的代码缺少一些东西(例如 Quake)。

MapViewUIKit 接受三个参数。

  • MKCoordinateRegion 的绑定,它必须是一个绑定,因为我们会将数据传回 ContentView
  • mapType是MKMapType,这个是为了改变地图类型
  • 注释,这是一种自定义 Annotation 类型,用于保存我们希望在地图上显示的注释的相关信息。
struct ContentView: View {

    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(
            latitude: 51.507222,
            longitude: -0.1275),
        span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
    )

    @State private var mapType: MKMapType = .standard

    var body: some View {
        VStack {
            Picker("", selection: $mapType) {
                Text("Standard").tag(MKMapType.standard)
                Text("Satellite").tag(MKMapType.satellite)
                Text("Hybrid").tag(MKMapType.hybrid)
            }
            .pickerStyle(SegmentedPickerStyle())

            MapViewUIKit(
                region: $region,
                mapType: mapType,
                annotation: Annotation(
                    title: "London",
                    coordinate: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275)
                )
            )

            HStack {
                Button {
                    region.span.latitudeDelta *= 0.5
                    region.span.longitudeDelta *= 0.5
                } label: {
                    HStack {
                        Image(systemName: "plus")
                    }
                }.padding(5)

                Button {
                    region.span.latitudeDelta /= 0.5
                    region.span.longitudeDelta /= 0.5
                } label: {
                    HStack {
                        Image(systemName: "minus")
                    }
                }.padding(5)
            }
        }

    }
}

这是我创建的 Annotation 结构,用于保存有关我们希望显示的注释的信息。

struct Annotation {
    let pointAnnotation: MKPointAnnotation

    init(title: String, coordinate: CLLocationCoordinate2D) {
        pointAnnotation = MKPointAnnotation()
        pointAnnotation.title = title
        pointAnnotation.coordinate = coordinate
    }
}

最后我们需要 UIViewRepresentable 将它们连接在一起。我在代码中添加了注释以说明它的作用。

struct MapViewUIKit: UIViewRepresentable {

    @Binding var region: MKCoordinateRegion
    let mapType : MKMapType
    let annotation: Annotation

    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.setRegion(region, animated: false)
        mapView.mapType = mapType

        // Set the delegate so that we can listen for changes and
        // act appropriately
        mapView.delegate = context.coordinator

        // Add the annotation to the map
        mapView.addAnnotation(annotation.pointAnnotation)
        return mapView
    }

    func updateUIView(_ mapView: MKMapView, context: Context) {
        mapView.mapType = mapType
        // Update your region so that it is now your new region
        mapView.setRegion(region, animated: true)
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapViewUIKit

        init(_ parent: MapViewUIKit) {
            self.parent = parent
        }

        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            // We should handle dequeue of annotation view's properly so we have to write this boiler plate.
            // This basically dequeues an MKAnnotationView if it exists, otherwise it creates a new
            // MKAnnotationView from our annotation.
            guard annotation is MKPointAnnotation else { return nil }

            let identifier = "Annotation"
            guard let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) else {
                let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
                annotationView.canShowCallout = true
                return annotationView
            }

            annotationView.annotation = annotation
            return annotationView
        }

        func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
            // We need to update the region when the user changes it
            // otherwise when we zoom the mapview will return to its original region
            DispatchQueue.main.async {
                self.parent.region = mapView.region
            }
        }
    }
}

这给出了以下输出

https://imgur.com/a/gH42UED

我建议您熟悉 Apple 的文档,那里有大量教程可以为您提供帮助。

https://developer.apple.com/documentation/mapkit/mkmapview https://developer.apple.com/documentation/mapkit/mkmapviewdelegate https://www.raywenderlich.com/7738344-mapkit-tutorial-getting-started https://www.hackingwithswift.com/example-code/location/how-to-add-annotations-to-mkmapview-using-mkpointannotation-and-mkpinannotationview https://talk.objc.io/episodes/S01E195-wrapping-map-view