I have a problem with clustering in MapKit, it doesn't work properly at start, I have to pan some time to force MapKit to "recalculate" clustering. The video below shows the problem.
https://youtu.be/5PK7uAV0F_8
My code's description
I have a class class MapKitViewController: UIViewController, MKMapViewDelegate with methods
override func loadView() {
super.loadView()
mapView = MKMapView(frame: view.bounds)
mapView.delegate = self
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
private func registerAnnotationViewClasses() {
mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(ClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
}
override func viewDidLoad() {
super.viewDidLoad()
registerAnnotationViewClasses()
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MyAnnotation {
let v = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
v.titleVisibility = .hidden
v.clusteringIdentifier = "clusterID"
return v
} else {
return nil
}
}
And this is my cluster annotation view class
class ClusterAnnotationView: MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
private func setUp() {
collisionMode = .circle
}
override func prepareForDisplay() {
super.prepareForDisplay()
displayPriority = .defaultHigh
if let clusterAnnotation = annotation as? MKClusterAnnotation {
image = image(forMembersCount: clusterAnnotation.memberAnnotations.count)
}
}
private func image(forMembersCount membersCount: Int) -> UIImage {
let size = /* calculate size */
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { _ in
/* draw an image */
}
}
}
What can I do to fix this issue?
in
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MyAnnotation {
let v = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
v.titleVisibility = .hidden
v.clusteringIdentifier = "clusterID"
return v
} else {
return nil
}
}
you are recreating all annotationViews, probably each time you are panning.
These are intended to be reused instead of recreated each time.
So mapView thinks you are replacing your annotationViews with new objects (that's what you are actually doing). This means there can't be a smooth transition of existing views - the old views don't exist any more.
See https://www.raywenderlich.com/7738344-mapkit-tutorial-getting-started#toc-anchor-009
after the title "Configuring the Annotation View"
on how to reuse annotationViews.
Related
There are approximately 1500 customized annotations with values and images.
I understand that this customization has a high price to be rendered in real time, but the UI is freezing(some time) during the viewfor annotation (when the user moves the map, each annotation takes time to render and more it zooms out, more annotations appear and the longer the UI freezes).
In the same application on android, I managed to improve this by clustering all the annotations out of sight, only rendering them when they became visible, without freezing the map.
Rendering must occur in the background, outside the main thread.
Other suggestions are also welcome.
Some code: viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
mapView.register(CustomPinAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
// mapView.register(CustomPinAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
... configureDatabase()
... loadMapMap()
... loadAnnotations()
}
more code: viewfor annotation
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isMember(of: MKUserLocation.self) { return nil }
if annotation is MKClusterAnnotation { return nil }
guard let customAnnotation = annotation as? CustomPin else { return nil }
... GET ANNOTATION INFO ...
let reusePin = preco+stared+fechado+excluido+publico+particular
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reusePin) as? CustomPinAnnotationView
if pinView == nil { pinView = CustomPinAnnotationView(annotation: annotation, reuseIdentifier: reusePin) }
else { return pinView }
... RENDER CUSTOM ANNOTATION IMAGE ...
UIGraphicsEndImageContext()
pinView?.canShowCallout = true
pinView?.centerOffset = CGPoint(x: size.width / 5 , y: -size.height / 5)
print("=", terminator:"")
return pinView
}
CustomPinAnnotationView.swift
import Foundation
import UIKit
import MapKit
class CustomPinAnnotationView: MKAnnotationView {
private var observerContext = 0
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
clusteringIdentifier = "cluster"
collisionMode = .circle
centerOffset = CGPoint(x: 0, y: -10) // Offset center point to animate better with marker annotations
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
willSet {
removeObserverIfAny()
}
didSet {
if let annotation = annotation as? CustomPin {
annotation.addObserver(self, forKeyPath: #keyPath(CustomPin.subtitle), context: &observerContext)
}
}
}
deinit {
removeObserverIfAny()
}
private func removeObserverIfAny() {
if let oldAnnotation = annotation as? CustomPin {
oldAnnotation.removeObserver(self, forKeyPath: #keyPath(CustomPin.subtitle))
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
}
}
I'm displaying custom annotations over the map and having hard times to receive didSelect calls on my delegate. Here is the code of the ViewController:
class TestAnnotationClickViewController: UIViewController, MGLMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MGLMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.delegate = self
mapView.addAnnotation(TestAnnotation())
view.addSubview(mapView)
}
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
if annotation is TestAnnotation {
let view = TestAnnotationView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
return view
}
return nil
}
func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
print("annotation didSelect")
}
func mapView(_ mapView: MGLMapView, didSelect annotationView: MGLAnnotationView) {
print("annotation view didSelect")
}
}
Here is the code for annotation class and corresponding view:
class TestAnnotation: NSObject, MGLAnnotation {
var coordinate: CLLocationCoordinate2D
override init() {
coordinate = CLLocationCoordinate2D(latitude: 33.9415889, longitude: -118.4107187)
}
}
class TestAnnotationView: MGLAnnotationView {
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
private func setupView() {
backgroundColor = .green
}
}
When I'm pressing the annotation (green rectangle) I expect delegate methods didSelect to be called. However neither of them is invoked. And the console doesn't get "annotation didSelect" or "annotation view didSelect" printed.
I also tried to set isUserInteractionEnabled on the TestAnnotationView but it didn't help. What am I missing?
I install Mapbox (5.9.0) via cocoapods:
pod 'Mapbox-iOS-SDK', '~> 5.9'
I tend to use reuseIdentifiers for the creation of annotations and construct an init that carries that and the annotation through so for your use-case, something like:
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
if annotation is TestAnnotation {
let view = TestAnnotationView(reuseIdentifier: "test", frame: CGRect(x: 0, y: 0, width: 100, height: 100), annotation: annotation)
return view
}
return nil
}
and in the TestAnnotationViewClass adding the initialiser:
init(reuseIdentifier: String?, frame: CGRect, annotation: MGLAnnotation) {
super.init(reuseIdentifier: reuseIdentifier)
self.frame = frame
setupView()
}
ensures that everything is setup so the annotations can respond to touches and trigger the didSelect delegate methods.
I am doing clustering of Annotations.
The below code works fine and clusters the points correctly in iOS 11 & iOS 12.
This fails to cluster the points decluster the points in iOS 13.
I am not using any beta versions.
The TTMapView class is wrapper for MKMapView.
class TTMapView: UIView {
var mapView = MKMapView()
private var mapObjects: Dictionary<TTShape, MKShape?> = [:]
private var _isClusteringEnabled = true
func addMarker(_ marker: TTPoint) -> TTPoint {
removeMarker(marker)
let coordinate = marker.coordinate
let pointAnnotation = MKPointAnnotation()
pointAnnotation.coordinate = convertTTCoordinateToCLLocationCoordinate2D(coordinate)
pointAnnotation.title = marker.title
pointAnnotation.subtitle = marker.subtitle
mapObjects.updateValue(pointAnnotation, forKey: marker)
mapView.addAnnotation(pointAnnotation)
return marker
}
}
extension TTMapView: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
if _isClusteringEnabled {
let point = mapObjects.filter ({ $0.value === annotation }).first?.key as? TTPoint
print("point ", point)
return TTClusterAnnotationView(annotation: annotation, reuseIdentifier: TTClusterAnnotationView.ReuseID, image: point?.image, color: point?.tintColor)
} else {
let reuseId = "simplePin"
var pinAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if pinAnnotationView == nil {
pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinAnnotationView?.isDraggable = true
pinAnnotationView?.canShowCallout = true
}
return pinAnnotationView
}
}
}
class TTClusterAnnotationView: MKMarkerAnnotationView {
/// Use this Id for setting annotation
static let ReuseID = "clusterAnnotation"
init(annotation: MKAnnotation?, reuseIdentifier: String?, image: UIImage?, color: UIColor? = nil) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
// Enable clustering by just setting the clusteringIdentifier
clusteringIdentifier = "clusteringIdentifier"
glyphImage = image
glyphTintColor = color
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForDisplay() {
super.prepareForDisplay()
displayPriority = .required
}
}
Make the cluster annotation view (this is the annotation view for the cluster, not to be confused with your existing annotation view that has a clusteringIdentifier, which I’ve renamed to CustomAnnotationView to avoid confusion) have a displayPriority of .required:
class CustomClusterAnnotationView: MKMarkerAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
displayPriority = .required
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
didSet {
displayPriority = .required
}
}
}
Then register that class:
mapView.register(CustomClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
And then use it:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
if annotation is MKClusterAnnotation {
return nil
}
...
}
A few unrelated observations:
I’d suggest just adding your image and color properties to a custom annotation type rather than having viewFor filter through mapObjects. So:
class CustomAnnotation: MKPointAnnotation {
let image: UIImage
let color: UIColor
init(coordinate: CLLocationCoordinate2D, title: String?, image: UIImage, color: UIColor) {
self.image = image
self.color = color
super.init()
self.coordinate = coordinate
self.title = title
}
}
Then, if you use that rather than MKPointAnnotation, your custom annotation view can pull the color and image info right out of the annotation.
class CustomAnnotationView: MKMarkerAnnotationView {
static let reuseID = "CustomAnnotationView"
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
updateForAnnotation()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
didSet {
updateForAnnotation()
}
}
}
private extension CustomAnnotationView {
func updateForAnnotation() {
clusteringIdentifier = "CustomAnnotationView"
displayPriority = .required
if let annotation = annotation as? CustomAnnotation {
glyphImage = annotation.image
glyphTintColor = annotation.color
} else {
glyphImage = nil
glyphTintColor = nil
}
}
}
Note, above I’m resetting the cluster identifier, image, glyph, etc. in the didSet of annotation. This enables the reuse of annotation views. (See next point.)
The reuse logic for your pin annotation view is not correct. And you’re not doing reuse at all if clustering is turned on. If targeting iOS 11 and later, I’d use dequeueReusableAnnotationView(withIdentifier:for:) which takes care of all of this for you. So, I can register this reuse id:
mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomAnnotationView.reuseID)
And I’d repeat that process for the “simple pin” annotation view that you show if you turn off clustering.
mapView.register(CustomSimplePinAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomSimplePinAnnotationView.reuseID)
And
class CustomSimplePinAnnotationView: MKPinAnnotationView {
static let reuseID = "CustomSimplePinAnnotationView"
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
isDraggable = true
canShowCallout = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And then your viewFor is simplified:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
switch annotation {
case is MKUserLocation:
return nil
case is MKClusterAnnotation:
return nil
default:
if _isClusteringEnabled {
return mapView.dequeueReusableAnnotationView(withIdentifier: CustomAnnotationView.reuseID, for: annotation)
} else {
return mapView.dequeueReusableAnnotationView(withIdentifier: CustomSimplePinAnnotationView.reuseID, for: annotation)
}
}
}
I want to have a custom MKAnnotationView. I've created a xib file in IB and set its class to MyAnnotationView.
class MyAnnotationView: 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")
}
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var busIcon: UIImageView!
}
Here's how the xib looks like - it has a textLabel and a busIcon linked:
I'm using the viewFor annotation delegate method to create views for all annotations:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MyAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationIdentifier") as? MyAnnotationView {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView = av
}
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.image = UIImage(named: "Delivery")
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
}
return annotationView
}
}
The annotationView.image = UIImage(named: "Delivery")
&
AnnotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
are there just to check if the code is working and display a sample view on the map, as they use the standard properties inherited from MKAnnotationView.
I don't know how to make the viewFor annotation method use the XIB I have created. Could anyone please help me with that? I searched for the solution, but only found something relevant in Obj C.
Thank you!
1- Create a view subclass of UIView with xib say CallView
2- Inside viewforAnnotation
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "id")
let customView = Bundle.main.loadNibNamed("CallView", owner: self, options: nil).first! as! CallView
// here configure label and imageView
annotationView.addSubview(customView)
UPDATED CODE BASED ON Sh-Khan's answer
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MyAnnotationView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MyAnnotationView
var annotationView: MyAnnotationView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
let customView = annotationView.subviews.first as! MyAnnotationView
customView.frame = annotationView.frame
customView.textLabel.text = (annotationView.annotation?.title)!
}
return annotationView
}
}
**Create Custom MKPointAnnotation Class**
import UIKit
import MapKit
class CustomPointAnnotation: MKPointAnnotation {
var id : Int
var url : String
init(id : Int , url : String ) {
self.id = id
self.url = url
}
}
Create Xib for MarkerView Class for Annotation View
class MarkerView: MKAnnotationView {
#IBOutlet weak var imgVwUser: UIImageView!
init(annotation: CustomPointAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if (hitView != nil)
{
self.superview?.bringSubviewToFront(self)
}
return hitView
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let rect = self.bounds
var isInside: Bool = rect.contains(point)
if(!isInside)
{
for view in self.subviews
{
isInside = view.frame.contains(point)
if isInside
{
break
}
}
}
return isInside
}
}
Add MKMapView Delegates In Your ViewController
extension YourViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MarkerView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MarkerView
var annotationView: MarkerView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MarkerView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MarkerView(annotation: annotation as? CustomPointAnnotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = false // callout bubble
if let annotation = annotation as? CustomPointAnnotation {
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 66, height: 75)
let customView = annotationView.subviews.first as? MarkerView
customView?.frame = annotationView.frame
let image = annotation.url
let imageUrl = URL(string: image)
customView?.imgVwUser.sd_setImage(with: imageUrl, placeholderImage: UIImage(named:"defaults"), options: [.refreshCached], completed: nil)
}
}
return annotationView
}
}
}
ADD Annotation to the mapview
extension YourViewController {
func addAnnotation(){
let annotationsToRemove = mapView.annotations.filter { $0 !== mapView.userLocation }
mapView.removeAnnotations( annotationsToRemove )
var annotations: [CustomPointAnnotation] = []
for i in 0..<self.arrayData.count {
let customPoints = CustomPointAnnotation.init(id: arrayData[i].id ?? 0, url: arrayData[i].url)
let location = CLLocationCoordinate2DMake(self.arrayData[i].lat ?? 0, self.arrayData[i].lng ?? 0)
customPoints.coordinate = location
annotations.append(customPoints)
}
mapView.addAnnotations(annotations)
}
}
I have the problem, that my Annotation is still a pin! I want the Button and the picture, for the information displayed about the User and it works. But it doesn't change the look of the pin on the Map!
This is my annotationView:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
// 1
let identifier = "pinAnnotation"
// 2
if annotation.isKindOfClass(pinAnnotation.self) {
// 3
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if annotationView == nil {
//4
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
// 5
let btn = UIButton(type: .InfoDark)
annotationView!.rightCalloutAccessoryView = btn
resultImgArray[0].getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let imageView = UIImageView(frame:CGRectMake(0,0,45,45))
imageView.image = UIImage(data: imageData!)
annotationView!.leftCalloutAccessoryView = imageView
}
}
} else {
// 6
annotationView!.annotation = annotation
}
return annotationView
}
return nil
}
This is my pinAnnotationView.swift as MKAnnotationView:
import UIKit
import MapKit
import CoreLocation
class pinAnnotationView: MKAnnotationView {
override init(frame :CGRect) {
super.init(frame: frame)
}
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
var frame = self.frame
frame.size = CGSizeMake(80, 80)
self.frame = frame
self.backgroundColor = UIColor.clearColor()
self.centerOffset = CGPointMake(-5, -5)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
UIImage(named: "carlogo.png")?.drawInRect(CGRectMake(30, 30, 30, 30))
}
}
Here is the image
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isMember(of: MKUserLocation.self) {
return nil
}
let identifier = "someID"
var dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if dequeuedAnnotationView == nil{
dequeuedAnnotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
configureDetailView(annotationView: dequeuedAnnotationView!)
}
return dequeuedAnnotationView
}
func configureDetailView(annotationView: MKAnnotationView) {
var frame = self.frame
frame.size = CGSizeMake(80, 80)
self.frame = frame
self.backgroundColor = UIColor.clearColor()
self.centerOffset = CGPointMake(-5, -5)
annotationView.image = UIImage(named:"yourImage")
}
And use annotationView.detailCalloutAccessoryView = "yourView" in this case will be frame.
I hope works for you