How to Create Custom Mkannotationview and Custom Annotation Title and Subtitle

How to create Custom MKAnnotationView and custom annotation title and subtitle

To create a custom annotation view (your replacement for the standard pin), you can just set the image property of the MKAnnotationView in the viewForAnnotation method:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
{
return nil;
}
else if ([annotation isKindOfClass:[YourAnnotationClassHere class]]) // use whatever annotation class you used when creating the annotation
{
static NSString * const identifier = @"MyCustomAnnotation";

MKAnnotationView* annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];

if (annotationView)
{
annotationView.annotation = annotation;
}
else
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:identifier];
}

annotationView.canShowCallout = NO; // set to YES if using customized rendition of standard callout; set to NO if creating your own callout from scratch
annotationView.image = [UIImage imageNamed:@"your-image-here.png"];

return annotationView;
}
return nil;
}

You might also want to adjust the centerOffset property to get the pin to line up precisely the way you want.

Regarding the customization of the callout, the easiest approach is to specify leftCalloutAccessoryView, rightCalloutAccessoryView and/or detailCalloutAccessoryView. This gives you a surprising degree of control, adding all sorts of images, labels, etc.

If you want to do a radical redesign of the callout, you can have viewForAnnotation set canShowCallout to NO and then respond to setSelected in your custom annotation view to show your own callout. While in Swift, see Customize MKAnnotation Callout View? for a few options for customizing the callouts.

Show Annotation Title and SubTitle in Custom MKPinAnnotationView

I was overriding one func

func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
mapView.deselectAnnotation(view.annotation, animated: true)
}

I commented this function out and got the titles and subtitles.

Updated Code

/*
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
mapView.deselectAnnotation(view.annotation, animated: true)
}
*/
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {

if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}

let reuseId = "pin"
let pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView.canShowCallout = true
pinView.animatesDrop = true
pinView.pinTintColor = UIColor.darkGrayColor()
pinView.draggable = true
pinView.accessibilityLabel = "hello"
let btn = UIButton(type: .DetailDisclosure)
pinView.rightCalloutAccessoryView = btn
return pinView
}

How to let user to add custom annotation?

That's how I solve this problem,

1) Create a UIView for user show where he wants to add an annotation.

2) Add a pan gesture recognizer in it.

func addPanGesture(view: UIView) {
let pan = UIPanGestureRecognizer(target: self, action: #selector (self.handlePan(sender:)))
view.addGestureRecognizer(pan)
}

3) In my selector func, I call pinDropped() func

  @objc func handlePan(sender: UIPanGestureRecognizer) {

let view = sender.view!
let translation = sender.translation(in: self.mapView)

switch sender.state {
case .began, .changed:
pinImage.center = CGPoint(x: dropPinImage.center.x + translation.x, y: dropPinImage.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: view)
break
default:
pinDropped()
break
}
}

4) I write what will be happening in my pinDropped func

func pinDropped() {
DispatchQueue.main.async {
let pin = CustomPin(self.lastOrigin, "pin")
self.mapView.addAnnotation(pin)
}
self.saveButton.alpha = 1
pinImage.alpha = 0
}

Custom Annotation Using MKPointAnnotation

You should implement delegate method:

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

if annotation.isKind(of: MKUserLocation.self) {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "User")
annotationView.image = UIImage(named: "icon")
return annotationView
}

let reuseId = "Image"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
annotationView?.canShowCallout = true
annotationView?.image = UIImage(named: "<<image name>>")
}
else {
annotationView?.annotation = annotation
}

return annotationView
}

Custom Annotation for Mapview Swift

There's nothing seriously wrong with the code. But there can be a couple of things that would cause problems, including:

  1. Have you set the delegate (either in IB or programmatically) for the map view? If not, your mapView(_:viewFor:) will never be called. Add breakpoint or debugging print statement to confirm.

  2. Have you confirmed that UIImage(named: "Skyscraper") is successfully retrieving an image? Make sure this is not returning nil.


Note, if only iOS 11 and later, you can simplify this code a bit. Since iOS 11, we no longer need for mapView(_:viewFor:) in simple scenarios like this. I would suggest putting the annotation view configuration code within the annotation view subclass, where it belongs, and avoid cluttering our view controller with a viewFor implementation.

So when you do get the current issue behind you, the recommended process is:

  1. Define classes for your annotation and annotation view:

    class CustomAnnotation: MKPointAnnotation {
    var pinCustomImage: UIImage!
    }

    And

    class CustomAnnotationView: MKAnnotationView {
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    canShowCallout = true
    update(for: annotation)
    }

    override var annotation: MKAnnotation? { didSet { update(for: annotation) } }

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

    private func update(for annotation: MKAnnotation?) {
    image = (annotation as? CustomAnnotation)?.pinCustomImage
    }
    }
  2. In viewDidLoad register this annotation view class:

    mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
  3. Remove mapView(_:viewFor:) implementation.

Now when you add a CustomAnnotation to your map’s list of annotations, it will be rendered correctly.

But I would suggest resolving your current problem first. There’s no point in refining you implementation until these more basic issues are resolved.

How can I create multiple custom annotations in a map view?

It's been a long time since I've used an MKMapView and custom annotations (it was in Objective-C.)

I seem to remember that in your mapView(_:viewFor:) function you need to test to see if the annotation being passed to you is an MKUserLocation. If it is, return nil:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard !annotation is MKUserLocation else {
//This is the user location. Return nil so the system uses the blue dot.
return nil
}
//Your code to create an return custom annotations)
}


Related Topics



Leave a reply



Submit