【问题标题】:SwiftUI: Saving MapKit Annotation Data to Pass To Another ViewSwiftUI:保存 MapKit 注释数据以传递到另一个视图
【发布时间】:2020-10-28 02:24:20
【问题描述】:

我正在努力保存 MapKit 注释数据(例如名称、地址、坐标等)并将其传递回具有地图视图链接的视图。我可以使用 didSelect 选择注释并将我要查找的数据打印到终端,但是我不知道如何将其传回。我一直在尝试使用 ObservedObject,但我收到一条错误消息:在隐式生成的 super.init 调用中未初始化属性“self.contentData”。

import SwiftUI
import MapKit

//MARK:- COORDINATOR
class Coordinator: NSObject, MKMapViewDelegate {
    
    //@ObservedObject var contentData: ContentData  // << deleted based on answer
    
    var control: MapUIView
    var contentData: ContentData  // << added based on answer
    
    init(_ control: MapUIView) {
        self.control = control
        self.contentData = control.contentData // << added based on answer
    }
    
    func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
        if let annotationView = views.first {
            if let annotation = annotationView.annotation {
                if annotation is MKUserLocation {
                    let region = MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 8000, longitudinalMeters: 8000)
                    mapView.setRegion(region, animated: true)
                }
            }
        }
    }
    
    func selectAnnotation(_ annotation: MKAnnotation, animated: Bool) {
        
    }
    
    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        if mapView.selectedAnnotations.count > 0 {
            if let selectedPinName = mapView.selectedAnnotations[0].title {
                print("\(selectedPinName!)")
            }
        }
        let region = MKCoordinateRegion(center: view.annotation!.coordinate, span: mapView.region.span)
        mapView.setRegion(region, animated: true)
        contentData.selectedLocationName = (mapView.selectedAnnotations[0].title as? String)!
    }

}

//MARK:- LOCATION MANAGER
class LocationManager: NSObject, ObservableObject {
    private let locationManager = CLLocationManager()
    @Published var location: CLLocation? = nil
    override init() {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.distanceFilter = kCLHeadingFilterNone
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }
}

extension LocationManager: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        print(status)
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else {
            return
        }
        self.location = location
    }
}

//MARK:- LANDMARK ANNOTATION
final class LandmarkAnnotation: NSObject, MKAnnotation {
    let title: String?
    let subtitle: String?
    let coordinate: CLLocationCoordinate2D
    
    init(landmark: Landmark) {
        self.title = landmark.name
        self.subtitle = landmark.title
        self.coordinate = landmark.coordinate
    }
}

//MARK:- LANDMARK
struct Landmark {
    let placemark: MKPlacemark
    var id: UUID {
        return UUID()
    }
    var name: String {
        self.placemark.name ?? ""
    }
    var title: String {
        self.placemark.title ?? ""
    }
    var coordinate: CLLocationCoordinate2D {
        self.placemark.coordinate
    }
}

//MARK:- VIEW
struct MapView: View {
    @ObservedObject var locationManager = LocationManager()
    @State private var landmarks: [Landmark] = [Landmark]()
    @State private var search: String = ""
    @State private var tapped: Bool = false
    @State private var showCancelButton: Bool = false
    
    @ObservedObject var contentData: ContentData

    private func getNearByLandmarks() {
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = search
        let search = MKLocalSearch(request: request)
        search.start { (response, error) in
            if let response = response {
                let mapItems = response.mapItems
                self.landmarks = mapItems.map {
                    Landmark(placemark: $0.placemark)
                }
            }
        }
    }
    
    var body: some View {
        ZStack(alignment: .top) {
            MapUIView(contentData: contentData, landmarks: landmarks)
            HStack {
                HStack {
                    Image(systemName: "magnifyingglass")
                    TextField("Search", text: $search, onEditingChanged: { _ in
                        self.showCancelButton = true
                        self.getNearByLandmarks()
                    }).foregroundColor(.primary)
                    Button(action: {
                        self.search = ""
                    }) {
                        Image(systemName: "xmark.circle.fill").opacity(search == "" ? 0 : 1)
                    }
                }
                .padding(EdgeInsets(top: 8, leading: 6, bottom: 8, trailing: 6))
                .foregroundColor(.secondary)
                .background(Color(.secondarySystemBackground))
                .cornerRadius(10.0)
            }
            .padding(EdgeInsets(top: 10, leading: 0, bottom: 1, trailing: 0))
            .padding(.horizontal)
        }
    }
}

//MARK:- MAPUIVIEW
struct MapUIView: UIViewRepresentable {
    @ObservedObject var contentData: ContentData
    let landmarks: [Landmark]
    func makeUIView(context: Context) -> MKMapView {
        let map = MKMapView()
        map.showsUserLocation = true
        map.delegate = context.coordinator
        return map
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapUIView>) {
        updateAnnotations(from: uiView)
    }
    func updateAnnotations(from mapView: MKMapView) {
        mapView.removeAnnotations(mapView.annotations)
        let annotations = self.landmarks.map(LandmarkAnnotation.init)
        mapView.addAnnotations(annotations)
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView(contentData: ContentData())
    }
}

调用地图视图:

NavigationLink(destination: MapView(contentData: contentData).edgesIgnoringSafeArea(.bottom), tag: 1, selection: $tag)

任何帮助将不胜感激。提前非常感谢。

编辑 ----------

在实施建议的更改之前和之后添加了注释行为的屏幕截图,以允许使用 ObservableObject 将注释数据传递回之前的 swiftui 视图。

【问题讨论】:

    标签: swiftui mapkit mkannotationview


    【解决方案1】:

    这里是固定部分

    class Coordinator: NSObject, MKMapViewDelegate {
        
        var contentData: ContentData  // << here !!
        var control: MapUIView
        
        init(_ control: MapUIView) {
            self.control = control
            self.contentData = control.contentData   // << here !!
        }
    
        // ... other code
    }
    

    注意:@ObservedObject 设计用于 SwiftUI 视图,在类中不需要

    【讨论】:

    • 我进行了这些更改,并且确实允许我将数据传回。非常感谢!但是,现在注释的行为不一样了。以前选择它们时,它们的尺寸会变大,并且该位置的地址出现在它们下方。现在选择时没有任何变化(请参阅添加到原始问题的屏幕截图)。如何让动画返回以查看选择了哪一个?有没有办法将注释传递回之前的 swiftui 视图,而不会改变选择行为(例如,比使用 ObservableObject 更好的方法将其传递回来)?
    猜你喜欢
    • 2014-12-20
    • 2021-09-14
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 2022-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多