首页 > 解决方案 > SwiftUI Bizarre MapKit makeUIView 行为

问题描述

我有一个 SwiftUI 应用程序,其中包含带有注释的地图和高速公路上的多段线叠加层。我使用 Core Data 来存储 Waypoint 结构来配置折线。多个航点进行一次旅行。

该应用程序是基本的 Master/Detail 样式,带有旅行列表和 DetailView,显示带有叠加层的地图。作为该过程的一部分,我计算每个路段(航点之间)的时间和距离。所有这些都按预期工作,但有一个非常奇怪的问题。

当我启动应用程序时,第一个行程以正确的时间和距离正确显示。但是,当单击第二个 Trip 时,会多次调用 UIViewRepresentable 的 makeUIView 函数——更奇怪的是,有五次。这显然浪费了互联网资源,并且使时间和距离不正确。现在如果这还不够奇怪,当点击第三次旅行时,信息是正确的。事实上,所有其他的 Trip 都是正确的——无论哪个是第一个,其他的都是正确的,而中间的都是错误的。我的第一个想法是我在某处有一些奇怪的切换,但我没有。我找不到任何需要 makeUIView 被调用五次的东西,当然不是每隔一次。

这是 DetailView 的代码:

struct DetailMapView: UIViewRepresentable {

    let kAppDelegate = UIApplication.shared.delegate as! AppDelegate

    @Environment(\.presentationMode) var presentationMode
    @ObservedObject var mdm: MyDefaultsManager

    @State var insertSequence : Int = 0
    @State var annotations: [MKAnnotation] = []
    @State var s: Int64 = 0
    @State var directionsArray: [MKDirections] = []

    var aTrip: Trip?

    @Binding var thumbImage: UIImage

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: DetailMapView
    
        init(_ parent: DetailMapView) {
            self.parent = parent
            parent.mdm.tripTimeAndDistance = "Waiting for servers..."
        }
    
        func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
            parent.mdm.pubCenterCoordinate = mapView.centerCoordinate
        }
    
        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            //unrelated code that always works
            return crsAnnotationView
        }//viewfor
    
        func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
            //print("hello - annotation view tapped")
        }
    
        func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
            //unrelated code that always works
        }//annotationView
    
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            let renderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
            renderer.strokeColor = UIColor.blue
            renderer.lineWidth = 4.0
        
            return renderer
        }//rendererFor
    }//class coordinator

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

    func makeUIView(context: Context) -> MKMapView {

        DispatchQueue.main.async {
            self.mdm.totalRouteDistance = 0.0
            self.mdm.totalTravelTime = 0.0
            self.mdm.callbackDistance = 0.0
            self.mdm.callbackTime = 0.0
        }
    
        self.mdm.showAddWaypointControls = false
    
        let mapView = MKMapView()
        mapView.showsUserLocation = true
        mapView.delegate = context.coordinator
        mdm.tripTimeAndDistance = "Calculating..."

        redrawTheMap2(trip: aTrip, mapView: mapView, callback2: callback2DistanceAndTime)

        return mapView
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        //no code here
    }//updateUIView

    func doOneWaypointPolylineCallback2(view: MKMapView, source: CLLocationCoordinate2D, destination: CLLocationCoordinate2D, callback: @escaping (Double, Double) -> Void) {
    
        var totDist = 0.0
        var totTrav = 0.0
    
        for anotation in annotations {
            view.deselectAnnotation(anotation, animated: false)
        }
        view.removeAnnotations(annotations)
    
        let request = MKDirections.Request()
        request.source = MKMapItem(placemark: MKPlacemark(coordinate: source))
        request.destination = MKMapItem(placemark: MKPlacemark(coordinate: destination))
    
        request.requestsAlternateRoutes = false
    
        request.transportType = mdm.walkDirections ? .walking : .automobile
    
        let directions = MKDirections(request: request)

        directions.calculate { response, error in
        
            guard let unwrappedResponse = response else {
                if let error = error {
                    print("Direction calulation failed: \(error.localizedDescription)")
                    self.mdm.tripTimeAndDistance = "The servers could not be reached. Please try later."
                    //throw an alert here to tell the user
                }
                return
            }//guard
        
            let route = unwrappedResponse.routes[0]
        
            view.addOverlay(route.polyline)
        
            totTrav += route.expectedTravelTime
            totDist += route.distance

            self.mdm.totalRouteDistance += totDist
            self.mdm.totalTravelTime += totTrav

            callback(self.mdm.totalRouteDistance, self.mdm.totalTravelTime)
        
        }
    
    }//doOneWaypoint Polyline Callback2

    func resetMapView(mapView: MKMapView, withNew directions: MKDirections) {
        mapView.removeOverlays(mapView.overlays)
        DispatchQueue.main.async {
            self.directionsArray.append(directions)
            let _ = self.directionsArray.map { $0.cancel() }
            self.directionsArray.removeAll()
        }
    }

    func chooseTransportType() -> MKDirectionsTransportType {
        //unrelated code that always works
    }//chooseTransportType

    func updateDistanceAndTimeTupleSingle(distance : Double, time : Double) {
    
        self.mdm.callbackDistance += distance
        self.mdm.callbackTime += time
    
        let totalDistance = NU.formatDistance(distanceMeters: distance, isMetric: self.mdm.metricDistance)
        let totalTime = NU.formatTime(timeInSec: time)
    
        self.mdm.tripTimeAndDistance  = "Combined distance is " + totalDistance +
            " and combined travel time is " + totalTime + "\n"

    }//update DistanceAndTime Tuple

    func redrawTheMap2(trip: Trip?, mapView: MKMapView, callback2: @escaping (Double, Double) -> Void) {

        mdm.redrawTheMap2Count += 1
        print("udm.redrawTheMap2Count is \(mdm.redrawTheMap2Count)")
    
        mapView.removeAnnotations(mapView.annotations)
        mapView.removeOverlays(mapView.overlays)
    
        guard let detail = aTrip else { return }
    
        for waypoint in detail.waypoints {
        
            let wp = waypoint as! Waypoint
            //print("waypoint sequence number = \(wp.sequence)")
            let coordinate = CLLocationCoordinate2D(latitude: (waypoint as! Waypoint).latitude, longitude: (waypoint as! Waypoint).longitude)
        
            //make an annotation from the waypoint information
            let annotation = AnnotationPin(wp: wp, title: "WP" + String(wp.sequence), subtitle: wp.name ?? "Subtitle", coordinate: coordinate)
        
            //this is the only place annotations are added to the detail view
            mapView.addAnnotation(annotation)
        
        }//for in
    
        let wpArray = Array(detail.waypoints).sorted(by: { ($0 as AnyObject).sequence < ($1 as AnyObject).sequence })
    
        var wpSourceSequence = 0
        var wpDestinationSequence = 1
    
        //for callback 2
        self.mdm.callbackDistance = 0.0
        self.mdm.callbackTime = 0.0
        //callback 2 above
    
        while wpDestinationSequence < wpArray.count {
            doOneWaypointPolylineCallback2(view: mapView,
                                           source: CLLocationCoordinate2D(latitude: (wpArray[wpSourceSequence] as! Waypoint).latitude, longitude: (wpArray[wpSourceSequence] as! Waypoint).longitude),
                                           destination: CLLocationCoordinate2D(latitude: (wpArray[wpDestinationSequence] as! Waypoint).latitude, longitude: (wpArray[wpDestinationSequence] as! Waypoint).longitude),
                                           callback: updateDistanceAndTimeTupleSingle)
            wpSourceSequence += 1
            wpDestinationSequence += 1

        }//while

        callback2(self.mdm.callbackDistance, self.mdm.callbackTime)
    
    }//redraw The Map 2

    func callback2DistanceAndTime (distance : Double, time : Double) {
        print("TryYo \(distance) and TryYo \(time)")
    }
}

顺便说一句 - 我前段时间(以及之前的几个 iOS 和 Xcode 版本)将该应用程序发布到了 App Store,我相信该应用程序当时的行为符合预期。

任何指导将不胜感激。Xcode 12.5 iOS 14.5

标签: iosswiftxcodemapkituiviewrepresentable

解决方案


推荐阅读