Subclassing MKGeodesicPolyline
Asked Answered
R

2

29

I'm trying to subclass MKPolyline and MKGeodesicPolyline to store their own individual colours (by having the subclass instances return their own MKPolylineRenderer). It works fine for MKPolyline, but the instances of my MKGeodesicPolyline subclass are not subclasses - simply MKGeodesicPolylines. Can anyone explain why? Here's my code...

protocol MapLineProtocol: MKOverlay {
    var width: CGFloat { get set }
    var colour: UIColor { get set }
}
extension MapLineProtocol {
    var renderer: MKPolylineRenderer {
        let polylineRenderer = MKPolylineRenderer(overlay: self)
        polylineRenderer.strokeColor = self.colour
        polylineRenderer.lineWidth = self.width
        return polylineRenderer
    }
}
class MapLine: MKPolyline, MapLineProtocol {
    var width: CGFloat = 3
    var colour: UIColor = .blue
    convenience init(start: CLLocationCoordinate2D, end: CLLocationCoordinate2D) {
        let line = [start, end]
        self.init(coordinates: line, count: line.count)
    }
}
class MapGeodesic: MKGeodesicPolyline, MapLineProtocol {
    var width: CGFloat = 3
    var colour: UIColor = .red
    convenience init(start: CLLocationCoordinate2D, end: CLLocationCoordinate2D) {
        let line = [start, end]
        self.init(coordinates: line, count: line.count)
    }
}

let mapLine = MapLine(start: loc.coordinate, end: end)
print("Mapline subclass: \(mapLine)") // <Appname.MapLine: xxx>
self.mapView.add(mapLine)
let geoLine = MapGeodesic(start: loc.coordinate, end: end)
print("Geodesic subclass: \(geoLine)") // <MKGeodesicPolyline: xxx> !!!
self.mapView.add(geoLine)

Accessing the .colour property on mapLine is fine (and the renderer works), but accessing .colour on the geoLine causes a run-time exception (and, of course, the renderer doesn't work if you bypass the colour). Can anyone please explain?

Ridgway answered 1/9, 2017 at 9:40 Comment(5)
What version of swift you are using? I wonder how your code could even compile.Billfold
Swift 4. And indeed, the real question is not how my code compiled, but what trickery does MapKit get up to to break Swift’s type checking so badly that we get a run time rather than compile time error!?Ridgway
I can't get this to work either in Xcode 12, subclassing with MKGeodesicPolyline makes the object MKGeodesicPolyline and not the subclass. Even if the object conforms to protocols it somehow just... no longer recognises that it conforms to them :|Electrophilic
related: #32349442Electrophilic
Definitely related @SparkyRobinson! - I found that 3 years ago when I first came across this, but it's all still a mystery. I guess the answer is that Obj-C "Class Clusters" that we were always warned not to try and mess with or subclass are really messy under the hood.Ridgway
S
1

As 2023 I have the exact same problem. I solved it by :

Subclassing MKPolyline as MKColorPolyline to have 2 fields, color and isGeodetic : Bool false by default

class MKColorPolyline : MKPolyline {
    var color :  UIColor = .red
    var isGeodesic : Bool = false
    
    convenience init(coordinates: UnsafePointer<CLLocationCoordinate2D>, count: Int, color: UIColor = .red, isGeodesic : Bool = false) {
        self.init(coordinates: coordinates, count: count)
        self.color = color
        self.isGeodesic = isGeodesic
    
        
    }
}

In the code creating the Polyline :

let polyline = MKColorPolyline(coordinates: arc.points.map({$0.coordinate}),
    count: arc.points.count, 
    color:  arc.selected ? .red : map.backTone == .light ? .systemBlue : .systemCyan,
    isGeodesic: true)
view.addOverlay(polyline)

In the delegate

let poly = overlay as! MKColorPolyline
var polylineRenderer : MKPolylineRenderer
                
if poly.isGeodesic{
   let geoPolyline = MKGeodesicPolyline(points: poly.points(), count: poly.pointCount)
   polylineRenderer = MKPolylineRenderer(polyline: geoPolyline)
}else {
    polylineRenderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
}
                
                
polylineRenderer.fillColor = UIColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 0.5)
polylineRenderer.strokeColor = (overlay as! MKColorPolyline).color
polylineRenderer.lineWidth = 2.0

Squarrose answered 16/2, 2023 at 9:29 Comment(0)
V
0

Am not able to catch exact issue.. but based on my analysis.. MKGeodesicPolyline is inherited from MKPolyline. So when you initialize MKGP will also initialize parent MKP, due to that using the Convenience Init makes some mess I think..

Valorievalorization answered 12/9, 2017 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.