ios - 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
解决方案
推荐阅读
- java - 总和 Firestore 字段的数组值
- ios - 应用商店连接。禁用新版本应用的应用内购买
- python - 来自 base64 的 PIL 打开图像失败
- python - <> 在beautifulsoup 中变成< >
- python - SyntaxError:无效的令牌
- mysql - 具有大量表的数据库中的弹性搜索中的数据摄取
- spring - spring mongodb 仅发送非空字段
- c - C 中大型问题(来自 32768)的并行 MPI 算法的二维数组的动态内存分配
- amazon-web-services - Redshift COPY 并自动创建表?
- javascript - JQuery Chrome 扩展,无法更改 css 属性