iOS MapKit bug using MKCircle overlays in satelliteFlyover and hybridFlyover MapTypes

陌路散爱 提交于 2020-05-15 21:46:32

问题


I think I've found a bug in MapKit when using satelliteFlyover and hybridFlyover MapTypes to display large paths across the globe. If you display both MKPolylines and MKCircles on the same map, as you zoom in you will see large sections of the overlays disappear.

I've seen the same basic bug since at least iOS 10. This sample code is for SwiftUI in iOS 13 with Xcode 11+. To duplicate this problem, just create a new SwiftUI project for iOS and copy all of this code into ContentView.swift. It includes a hard-coded array of 124 route points which it draws 1 MKPolyLine for the entire route, and an MKCircle at each segment of the route. It sets the MapView to fit the entire route into the view. Double-tap on the route to zoom in and you should immediately see large sections of the route overlay disappear. If you don't see it, play around with the zoom and position, and it should happen in a short amount of time.

Very similar stuff happens in a "classic" Swift project, but I've only included the sample code for SwiftUI.

If you comment out the view.addOverlays(circles) code, or switch to a non-flyover map type, the bug goes away. I've searched and found many similar questions, but none with the sample code I've provided and no answers. I've also included a picture of the bug showing the full route, and after zooming in once via double-tap.

import SwiftUI
import MapKit
import Combine

struct ContentView: View {
    var body: some View {
        OverlayMapView()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct OverlayMapView: UIViewRepresentable {
    func makeCoordinator() -> OverlayMapViewCoordinator{
        return OverlayMapViewCoordinator(self)
    }

    // COORDINATES OF THE ROUTE I WILL DISPLAY ON MAP VIEW //
    let trackString = "[ { \"latitude\": 52.5171545, \"longitude\": 13.3887787 }, { \"latitude\": 52.5144247, \"longitude\": 13.3479973 }, { \"latitude\": 52.5111832, \"longitude\": 13.2990761 }, { \"latitude\": 52.4571290, \"longitude\": 13.2212324 }, { \"latitude\": 52.3886362, \"longitude\": 13.1556653 }, { \"latitude\": 52.3036505, \"longitude\": 13.0856312 }, { \"latitude\": 52.2584992, \"longitude\": 12.9075180 }, { \"latitude\": 52.1584012, \"longitude\": 12.7816617 }, { \"latitude\": 52.0279639, \"longitude\": 12.6022086 }, { \"latitude\": 51.9169913, \"longitude\": 12.4386114 }, { \"latitude\": 51.8204921, \"longitude\": 12.3072065 }, { \"latitude\": 51.6116131, \"longitude\": 12.1869201 }, { \"latitude\": 51.4433886, \"longitude\": 12.2070987 }, { \"latitude\": 51.2786262, \"longitude\": 12.1187881 }, { \"latitude\": 51.0812058, \"longitude\": 11.9554253 }, { \"latitude\": 50.9621299, \"longitude\": 11.8548381 }, { \"latitude\": 50.8273297, \"longitude\": 11.8804931 }, { \"latitude\": 50.7379517, \"longitude\": 11.8522551 }, { \"latitude\": 50.6222699, \"longitude\": 11.7929304 }, { \"latitude\": 50.5300081, \"longitude\": 11.8034421 }, { \"latitude\": 50.4385435, \"longitude\": 11.8027631 }, { \"latitude\": 50.3895479, \"longitude\": 11.7741292 }, { \"latitude\": 50.3307057, \"longitude\": 11.7892785 }, { \"latitude\": 50.2378294, \"longitude\": 11.7778346 }, { \"latitude\": 50.1559121, \"longitude\": 11.7493025 }, { \"latitude\": 50.0852997, \"longitude\": 11.6738623 }, { \"latitude\": 50.0208811, \"longitude\": 11.6040941 }, { \"latitude\": 49.9476147, \"longitude\": 11.6069619 }, { \"latitude\": 49.8821142, \"longitude\": 11.5620916 }, { \"latitude\": 49.8155086, \"longitude\": 11.4975817 }, { \"latitude\": 49.7389562, \"longitude\": 11.5151384 }, { \"latitude\": 49.6499968, \"longitude\": 11.4416794 }, { \"latitude\": 49.5820874, \"longitude\": 11.3449900 }, { \"latitude\": 49.4509216, \"longitude\": 11.2356317 }, { \"latitude\": 49.3051689, \"longitude\": 11.2078149 }, { \"latitude\": 49.1216054, \"longitude\": 11.2763649 }, { \"latitude\": 49.0279475, \"longitude\": 11.3652675 }, { \"latitude\": 48.9659557, \"longitude\": 11.4131542 }, { \"latitude\": 48.8697751, \"longitude\": 11.4673624 }, { \"latitude\": 48.7483854, \"longitude\": 11.4589006 }, { \"latitude\": 48.6080374, \"longitude\": 11.5454441 }, { \"latitude\": 48.5231078, \"longitude\": 11.5840499 }, { \"latitude\": 48.4300317, \"longitude\": 11.5892578 }, { \"latitude\": 48.2631234, \"longitude\": 11.6466616 }, { \"latitude\": 48.1978131, \"longitude\": 11.6735754 }, { \"latitude\": 48.1068283, \"longitude\": 11.7542135 }, { \"latitude\": 48.0285778, \"longitude\": 11.6617551 }, { \"latitude\": 47.8996669, \"longitude\": 11.7210849 }, { \"latitude\": 47.8536071, \"longitude\": 11.8612033 }, { \"latitude\": 47.8220789, \"longitude\": 11.9689706 }, { \"latitude\": 47.7769133, \"longitude\": 12.0942786 }, { \"latitude\": 47.6408800, \"longitude\": 12.1926944 }, { \"latitude\": 47.5804035, \"longitude\": 12.1499337 }, { \"latitude\": 47.5294446, \"longitude\": 12.0792858 }, { \"latitude\": 47.4838435, \"longitude\": 12.0230287 }, { \"latitude\": 47.4579054, \"longitude\": 11.9192867 }, { \"latitude\": 47.4139132, \"longitude\": 11.8469363 }, { \"latitude\": 47.3862922, \"longitude\": 11.7789783 }, { \"latitude\": 47.3544607, \"longitude\": 11.6980531 }, { \"latitude\": 47.3094880, \"longitude\": 11.6302326 }, { \"latitude\": 47.2793423, \"longitude\": 11.5394188 }, { \"latitude\": 47.2620721, \"longitude\": 11.4484583 }, { \"latitude\": 47.2479609, \"longitude\": 11.4009057 }, { \"latitude\": 47.2057609, \"longitude\": 11.4038957 }, { \"latitude\": 47.1827700, \"longitude\": 11.4094292 }, { \"latitude\": 47.1224566, \"longitude\": 11.4490835 }, { \"latitude\": 47.0529721, \"longitude\": 11.4813358 }, { \"latitude\": 47.0014596, \"longitude\": 11.5067679 }, { \"latitude\": 46.9628561, \"longitude\": 11.4635759 }, { \"latitude\": 46.9282823, \"longitude\": 11.4466873 }, { \"latitude\": 46.8937451, \"longitude\": 11.4239701 }, { \"latitude\": 46.8681120, \"longitude\": 11.4849441 }, { \"latitude\": 46.8440770, \"longitude\": 11.5235225 }, { \"latitude\": 46.8120883, \"longitude\": 11.5588309 }, { \"latitude\": 46.7862000, \"longitude\": 11.6156863 }, { \"latitude\": 46.7556600, \"longitude\": 11.6338769 }, { \"latitude\": 46.7178259, \"longitude\": 11.6466731 }, { \"latitude\": 46.6651770, \"longitude\": 11.6128725 }, { \"latitude\": 46.6400183, \"longitude\": 11.5727892 }, { \"latitude\": 46.5998648, \"longitude\": 11.5336546 }, { \"latitude\": 46.5372412, \"longitude\": 11.5001471 }, { \"latitude\": 46.4933929, \"longitude\": 11.4384742 }, { \"latitude\": 46.4893379, \"longitude\": 11.3468002 }, { \"latitude\": 46.3977515, \"longitude\": 11.2975130 }, { \"latitude\": 46.3186299, \"longitude\": 11.2639896 }, { \"latitude\": 46.2250601, \"longitude\": 11.1489899 }, { \"latitude\": 46.0937106, \"longitude\": 11.0984695 }, { \"latitude\": 45.9271224, \"longitude\": 11.0770149 }, { \"latitude\": 45.7986273, \"longitude\": 11.0201558 }, { \"latitude\": 45.7310138, \"longitude\": 10.9441862 }, { \"latitude\": 45.6396983, \"longitude\": 10.8779285 }, { \"latitude\": 45.5566058, \"longitude\": 10.7997523 }, { \"latitude\": 45.4536325, \"longitude\": 10.8634169 }, { \"latitude\": 45.2780416, \"longitude\": 10.8793776 }, { \"latitude\": 45.0477843, \"longitude\": 10.8504319 }, { \"latitude\": 44.8871400, \"longitude\": 10.8502742 }, { \"latitude\": 44.7189028, \"longitude\": 10.8460946 }, { \"latitude\": 44.6383965, \"longitude\": 10.8723447 }, { \"latitude\": 44.4648136, \"longitude\": 11.2746187 }, { \"latitude\": 44.3137510, \"longitude\": 11.2506693 }, { \"latitude\": 44.1873659, \"longitude\": 11.1986969 }, { \"latitude\": 44.0384894, \"longitude\": 11.2265951 }, { \"latitude\": 43.9505945, \"longitude\": 11.2133675 }, { \"latitude\": 43.9116671, \"longitude\": 11.1956721 }, { \"latitude\": 43.8349429, \"longitude\": 11.1586810 }, { \"latitude\": 43.7295248, \"longitude\": 11.1931630 }, { \"latitude\": 43.7331218, \"longitude\": 11.3027752 }, { \"latitude\": 43.6940864, \"longitude\": 11.4199792 }, { \"latitude\": 43.5758158, \"longitude\": 11.5351903 }, { \"latitude\": 43.5048507, \"longitude\": 11.6495378 }, { \"latitude\": 43.4464880, \"longitude\": 11.7668015 }, { \"latitude\": 43.2706279, \"longitude\": 11.7689322 }, { \"latitude\": 43.0591872, \"longitude\": 11.9018270 }, { \"latitude\": 42.8628297, \"longitude\": 12.0348085 }, { \"latitude\": 42.7405131, \"longitude\": 12.0916594 }, { \"latitude\": 42.6582831, \"longitude\": 12.2238333 }, { \"latitude\": 42.4653563, \"longitude\": 12.3845467 }, { \"latitude\": 42.3510139, \"longitude\": 12.4712664 }, { \"latitude\": 42.2752562, \"longitude\": 12.5366847 }, { \"latitude\": 42.1811099, \"longitude\": 12.6021535 }, { \"latitude\": 42.0413732, \"longitude\": 12.6597238 }, { \"latitude\": 41.9346814, \"longitude\": 12.7347663 }, { \"latitude\": 41.9158391, \"longitude\": 12.6025870 }, { \"latitude\": 41.8947200, \"longitude\": 12.5172805 } ]"
    // STRING TO [DICTIONARY] CONVERTER FROM
    // https://stackoverflow.com/questions/45269962/how-to-convert-string-to-array-of-array-of-dictionaries
    func fromJSON(string: String) throws -> [[String: Any]] {
        let data = string.data(using: .utf8)!
        guard let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyObject] else {
            throw NSError(domain: NSCocoaErrorDomain, code: 1, userInfo: [NSLocalizedDescriptionKey: "Invalid JSON"])
        }
        return jsonObject.map { $0 as! [String: Any] }
    }

    func updateUIView(_ view: MKMapView, context: Context){

        view.delegate = context.coordinator

        // ONLY "FLYOVER" MAP TYPES HAVE THE BUG //
        view.mapType = MKMapType.satelliteFlyover   // BUG
//        view.mapType = MKMapType.hybridFlyover   // BUG
//        view.mapType = MKMapType.satellite   // NO BUG
//        view.mapType = MKMapType.mutedStandard   // NO BUG
//        view.mapType = MKMapType.standard   // NO BUG
//        view.mapType = MKMapType.hybrid   // NO BUG

        // HARD-CODE THE MAP VIEW REGION //
        let span = MKCoordinateSpan(latitudeDelta: 14.0,
                                    longitudeDelta: 5.0)
        let coordinate = CLLocationCoordinate2D(
            latitude: 47.2059375,
            longitude: 12.0942655)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        view.setRegion(region, animated: true)

        // CONVERT ROUTE FROM STRING TO POLYLINES AND CIRCLES
        let tracks = try! fromJSON(string: trackString)
        var locations = [CLLocationCoordinate2D]()
        var circles = [MKCircle]()
        for track in tracks {
            let lat = track["latitude"] as! CLLocationDegrees
            let lng = track["longitude"] as! CLLocationDegrees
            locations.append(CLLocationCoordinate2DMake(lat, lng))
            circles.append(MKCircle(center: CLLocationCoordinate2DMake(lat, lng),
                     radius: 40.0))
        }
        let polyline = MKPolyline(coordinates: locations, count: locations.count)

        view.addOverlay(polyline)
        // ADDING CIRCLES SHOWS THE BUG //
        // HIDING CIRCLES HIDS THE BUG //
        view.addOverlays(circles)
    }

    func makeUIView(context: Context) -> MKMapView{
        MKMapView(frame: .zero)
    }
}



// MAP VIEW COORDINATOR SAMPLE CODE TAKEN FROM THIS BLOG:
// https://medium.com/better-programming/exploring-mapkit-on-ios-13-1a7a1439e3b6
class OverlayMapViewCoordinator: NSObject, MKMapViewDelegate {
    var mapViewController: OverlayMapView

    init(_ control: OverlayMapView) {
        self.mapViewController = control
    }

    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKMultiPolyline{
            let renderer = MKPolylineRenderer(overlay:overlay)
            renderer.strokeColor = UIColor.red
            renderer.lineWidth = 4
            return renderer
        } else if overlay is MKPolyline {
            let renderer = MKPolylineRenderer(overlay:overlay)
            renderer.strokeColor = UIColor.purple
            renderer.lineWidth = 4
            return renderer
        } else if overlay is MKCircle {
            let renderer = MKCircleRenderer(overlay: overlay)
            renderer.strokeColor = UIColor.init(hue: 0.55556, saturation: 0.85, brightness: 0.94, alpha: 0.75)
            renderer.lineWidth = 10
            return renderer
        }


        return MKOverlayRenderer(overlay: overlay)
    }
}

Image showing example of the bug

来源:https://stackoverflow.com/questions/60974286/ios-mapkit-bug-using-mkcircle-overlays-in-satelliteflyover-and-hybridflyover-map

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!