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)
}
}
}
Related
I have to change image of my pin annotation in MapKit. And there should be two pin. One of these should be current location, second one is selector new location
func mapView(\_ mapView: MKMapView, viewFor annotation: MKAnnotation) -\> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "customannotation")
annotationView.image = UIImage(named:"pin")
annotationView.canShowCallout = true
return annotationView
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("annotation title == \(String(describing: view.annotation?.title!))")
}
You said:
I have to change image of my pin annotation in MapKit.
If you are saying that you have an annotation showing its associated annotation view on the map, and you want to change the image for that annotation view, you can add a property to your annotation to indicate which image is to be shown, and then have a custom MKAnnotationView object which observes the annotation’s property and updates its image, accordingly.
For example, consider an annotation that has an imageName property:
class CustomAnnotation: MKPointAnnotation {
#objc dynamic var imageName: String
init(
coordinate: CLLocationCoordinate2D,
title: String? = nil,
subtitle: String? = nil,
imageName: String
) {
self.imageName = imageName
super.init()
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
Note, I made that an #objc dynamic property so that I can have an annotation view that observes that image name change:
class CustomAnnotationView: MKAnnotationView {
static let reuseIdentifier = "CustomAnnotationView"
override var annotation: MKAnnotation? {
didSet { addObserver(for: annotation) }
}
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
addObserver(for: annotation)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var observer: NSKeyValueObservation?
func addObserver(for annotation: MKAnnotation?) {
let annotation = annotation as? CustomAnnotation
let imageName = annotation?.imageName ?? "red"
updateImage(named: imageName)
observer = annotation?.observe(\.imageName) { [weak self] annotation, _ in
self?.updateImage(named: annotation.imageName)
}
}
private func updateImage(named name: String) {
let image = UIImage(named: name)
self.image = image
// the following is only needed if you want the bottom of the
// image over the coordinates (e.g., you are using a “pin”).
if let image = image {
centerOffset = CGPoint(x: 0, y: -image.size.height / 2)
}
}
}
So, you can create the annotation:
let annotation = CustomAnnotation(coordinate: coordinate, imageName: "red")
mapView.addAnnotation(annotation)
And when you want, you can update the imageName associated for that annotation, and the annotation view will observe that value change, and update the image accordingly:
annotation.imageName = "black"
That yields:
And there should be two pin. One of these should be current location, second one is selector new location
There is a special annotation for the user location, the MKUserLocation. Right now, your mapView(_:viewFor:) is returning nil for this annotation. Just change your viewFor to return whatever annotation you want for the MKUserLocation rather than nil.
Obviously this assumes (a) that you have enabled showsUserLocation property for your mapView and (b) that you have requested permission for the user’s location. E.g.:
private let manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if manager.authorizationStatus == .notDetermined {
manager.requestWhenInUseAuthorization()
}
mapView.showsUserLocation = true
...
}
And of course, set the appropriate privacy strings in Info.plist:
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.
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 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