Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'NSInvalidArgumentException' when using MapKit

Tags:

ios

mapkit

i'm creating a simple view controller with a map and 100-200 MKPointAnnotation using the iOS 11 MKMarkerAnnotationView

This is the viewDidLoad of the controller

 override func viewDidLoad() {
    super.viewDidLoad()
    self.mapView.register(StationAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
    self.mapView.register(StationClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)

    locationDelegate.delegate = self
    self.mapView.delegate = self
    self.mapView.showsUserLocation = true
    self.refreshData()
    self.establishUserPosition()
}

Then i download the stations from a JSON (network object) and i add all of them to the mapview

func reloadViews(){
    if let network = network{
        for station in network.stations{
            let annotation = StationAnnotation(station: station)
            annotations.append(annotation) // I add the annotations to an array to prevent them to be deallocated
            mapView.addAnnotation(annotation)
        }
    }
}

This is my personal annotation

class StationAnnotation : MKPointAnnotation{
var station : Station?
var tintColor : UIColor?{
    if self.station?.free_bikes ?? 0 > 0 {
        return .green
    }else{
        return .red
    }
}

var glyphImage : UIImage?{
    if self.station?.extra.status == "online"{
        return UIImage(named: "Bicycle")
    }else{
        return UIImage(named: "Ban")
    }
}

override init() {
    super.init()
}

convenience init(station : Station){
    self.init()
    self.title = station.name
    self.coordinate = CLLocationCoordinate2D(latitude: station.latitude, longitude: station.longitude)
    self.station = station
    if station.extra.status == "online"{
        self.subtitle =  "Bikes: \(station.free_bikes) - Slots: \(station.empty_slots)"
    }else{
        self.subtitle = station.extra.status
    }
}
}

And my customs Views

class StationAnnotationView : MKMarkerAnnotationView{

override var annotation: MKAnnotation? {
    willSet {
        if let annotation = newValue as? StationAnnotation{

            self.markerTintColor = annotation.tintColor
            self.clusteringIdentifier = "station"
            self.glyphImage = annotation.glyphImage
        }
    }
}
}


class StationClusterView: MKAnnotationView {

override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override var annotation: MKAnnotation? {
    willSet {
        if let cluster = newValue as? MKClusterAnnotation {
            let renderer = UIGraphicsImageRenderer(size: CGSize(width: 40, height: 40))
            let count = cluster.memberAnnotations.count
            let onlineCount = cluster.memberAnnotations.filter { member -> Bool in
                return (member as! StationAnnotation).station?.extra.status == "online"
                }.count
            image = renderer.image { _ in
                // Fill full circle with tricycle color
                UIColor(named: "Forbidden")?.setFill()
                UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 40, height: 40)).fill()

                // Fill pie with unicycle color
                UIColor(named: "Available")?.setFill()
                let piePath = UIBezierPath()
                piePath.addArc(withCenter: CGPoint(x: 20, y: 20), radius: 20,
                               startAngle: 0, endAngle: (CGFloat.pi * 2.0 * CGFloat(onlineCount)) / CGFloat(count),
                               clockwise: true)
                piePath.addLine(to: CGPoint(x: 20, y: 20))
                piePath.close()
                piePath.fill()

                // Fill inner circle with white color
                UIColor.white.setFill()
                UIBezierPath(ovalIn: CGRect(x: 8, y: 8, width: 24, height: 24)).fill()

                // Finally draw count text vertically and horizontally centered
                let attributes = [ NSAttributedStringKey.foregroundColor: UIColor.black,
                                   NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 20)]
                let text = "\(count)"
                let size = text.size(withAttributes: attributes)
                let rect = CGRect(x: 20 - size.width / 2, y: 20 - size.height / 2, width: size.width, height: size.height)
                text.draw(in: rect, withAttributes: attributes)
            }
        }
    }
}

}

I don't know why the app while pinching , zooming, or panning, crash with SIGABRT signal and this exception

*** Terminating app due to uncaught exception 
'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKey:]: key cannot be nil'

I've tried every kind of debug system and the use of exception breakpoint didn't helped... have you any suggestions?

like image 333
Giorgio Romano Avatar asked Nov 22 '25 16:11

Giorgio Romano


1 Answers

Hllo everybody, i find solutions. At first - it s..t happens when we use

mapView.register(AnyClass?, forAnnotationViewWithReuseIdentifier: String) 

and

mapView.dequeueReusableAnnotationView(withIdentifier: String) 

returns nil.

So hot fix: Add:

ViewController: UIViewController, MKMapViewDelegate  

add

    override func viewDidLoad() {  
        super.viewDidLoad()  
        mapView.delegate = self  

        mapView.register(MarkerPointView.self, forAnnotationViewWithReuseIdentifier: "marker")  
        mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: "cluster")  
}  

and finaly:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {  

        if let marker = annotation as? MarkerAnnotation{  
            var view = mapView.dequeueReusableAnnotationView(withIdentifier: "marker") as? MarkerPointView  
            if view == nil {  
//Very IMPORTANT  
                print("nil for Marker")  
                view = MarkerPointView(annotation: marker, reuseIdentifier: "marker")  
            }  
            return view  
        }else if let cluster = annotation as? MKClusterAnnotation{  
            var view = mapView.dequeueReusableAnnotationView(withIdentifier: "cluster") as? ClusterView  
            if view == nil{  
//Very IMPORTANT  
                print("nil for Cluster")  
                view = ClusterView(annotation: cluster, reuseIdentifier: "cluster")  
            }  
            return view  
        }  
        else{  
            return nil  
        }  
    }  

hope it's help for somebody, and on next revs apple fix it, because we can use it like they said on wwdc2017 on 36:50 - we CAN'T delete it!!!!!!!!

original post on forums.developer.apple.com

like image 174
Andrew DevNir Avatar answered Nov 24 '25 08:11

Andrew DevNir



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!