Reputation: 13
When annotation is displayed on the map, it can be built and executed, but an error occurs during execution and it stops. MKMarkerAnnotationView displays clustering and glyph image. There are about 100 annotations, which are acquired from Firestore and stored in an array when View is loaded. The error occurred when the map was enlarged or moved. Swift version: Swift5 I also checked the outlet connection, but it looks like there is no problem.
Can't find a solution and can you tell me what's wrong? I wrote about the setting method of Map, etc., but I would appreciate if you could teach me if it is fundamentally wrong.
Error [XXX.CustomPinAnnotation memberAnnotations]: unrecognized selector sent to instance 0x2835e16c0
CustomPinAnnotation.swift
import UIKit
import MapKit
class CustomPinAnnotation: NSObject, MKAnnotation {
let clusteringIdentifier : String
let title: String?
let subtitle: String?
let coordinate: CLLocationCoordinate2D
//let glyphText: String
let glyphImage: UIImage
let glyphTintColor: UIColor
let markerTintColor: UIColor
let objectid: Int
init(_ clusteringIdentifier: String, title: String, subtitle: String, coordinate: CLLocationCoordinate2D, glyphImage: UIImage, glyphTintColor: UIColor, markerTintColor: UIColor, objectid: Int) {
self.clusteringIdentifier = clusteringIdentifier
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
// self.glyphText = glyphText
self.glyphImage = glyphImage
self.glyphTintColor = glyphTintColor
self.markerTintColor = markerTintColor
self.objectid = objectid
}
}
ViewController.swift
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
var locationManager = CLLocationManager()
var userCoordinate = CLLocationCoordinate2D()
//object array
var objectAll = [objectData]()
//Map view
@IBOutlet weak var mapView: MKMapView!
func displayAllMountains() {
for mountain in objectAll {
let pinImage = UIImage.init(named: "XXXXX")!
let subtitletext = String(object.height) + "m"
let annotation = CustomPinAnnotation("clusterid", title:object.name, subtitle: subtitletext, coordinate: object.geopoint, glyphImage: pinImage, glyphTintColor: .white, markerTintColor: .darkGray, objectid: object.objectid)
self.mapView.addAnnotation(annotation)
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation === mapView.userLocation {
return nil
} else {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier, for: annotation)
guard let markerAnnotationView = annotationView as? MKMarkerAnnotationView,
let annotation = annotation as? CustomPinAnnotation else {
return annotationView
}
markerAnnotationView.clusteringIdentifier = annotation.clusteringIdentifier
// markerAnnotationView.glyphText = annotation.glyphText
markerAnnotationView.glyphImage = annotation.glyphImage
markerAnnotationView.glyphTintColor = annotation.glyphTintColor
markerAnnotationView.markerTintColor = annotation.markerTintColor
return markerAnnotationView
}
}
}
Upvotes: 1
Views: 503
Reputation: 437372
You say it’s reporting:
Error [XXX.CustomPinAnnotation memberAnnotations]: unrecognized selector sent to instance 0x2835e16c0
The memberAnnotations
is a MKClusterAnnotation
method. But it’s trying to call that method on your CustomPinAnnotation
object. Somewhere, it’s trying to use a CustomPinAnnotation
in a context where it was expecting a MKClusterAnnotation
.
I’ve had troubles manifesting your problem, and I’m not sure you’ve shared enough for us to reproduce it. But I can easily see how it get be confused. There are three annotation types, not only MKUserLocation
and CustomPinAnnotation
, but also MKClusterAnnotation
. Your mapView(_:viewFor:)
only contemplates the existence of the first two types. You can make it handle MKClusterAnnotation
, too, if you want:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
} else if annotation is MKClusterAnnotation {
return mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier, for: annotation)
}
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier, for: annotation)
guard
let markerAnnotationView = annotationView as? MKMarkerAnnotationView,
let annotation = annotation as? CustomPinAnnotation
else {
return annotationView
}
markerAnnotationView.clusteringIdentifier = annotation.clusteringIdentifier
markerAnnotationView.displayPriority = .required
// markerAnnotationView.glyphText = annotation.glyphText
markerAnnotationView.glyphImage = annotation.glyphImage
markerAnnotationView.glyphTintColor = annotation.glyphTintColor
markerAnnotationView.markerTintColor = annotation.markerTintColor
return markerAnnotationView
}
Personally, though, I'd suggest removing mapView(_:viewFor:)
entirely, and instead define your own annotation view class that does this configuration:
class CustomAnnotationView: MKMarkerAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
update(for: annotation)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? { didSet { update(for: annotation) } }
func update(for annotation: MKAnnotation?) {
displayPriority = .required
guard let annotation = annotation as? CustomPinAnnotation else { return }
clusteringIdentifier = annotation.clusteringIdentifier
// markerAnnotationView.glyphText = annotation.glyphText
glyphImage = annotation.glyphImage
glyphTintColor = annotation.glyphTintColor
markerTintColor = annotation.markerTintColor
}
}
And, of course, make sure to register it (and your cluster annotation view, too):
mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
But the key is that the default implementation of mapView(_:viewFor:)
handles all three types of annotation views automatically. And this way, we take gritty “how do I render this annotation view” code out of the view controller (or whatever the MKMapViewDelegate
was) and into the annotation view class, where it belongs.
Upvotes: 1