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:
Have you set the
delegate
(either in IB or programmatically) for the map view? If not, yourmapView(_:viewFor:)
will never be called. Add breakpoint or debuggingprint
statement to confirm.Have you confirmed that
UIImage(named: "Skyscraper")
is successfully retrieving an image? Make sure this is not returningnil
.
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:
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
}
}In
viewDidLoad
register this annotation view class:mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
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
Run Timer in 'Background' When App Is Closed
How to Programmatically Fake a Touch Event to a Uibutton
How to List Out All the Subviews in a Uiviewcontroller in iOS
How Does Apple Notify iOS Apps of Refunds of In-App Purchases (Iap)
Gradient Polyline with Mapkit iOS
Ios6 Udid - What Advantages Does Identifierforvendor Have Over Identifierforadvertising
Updating iOS Badge Without Push Notifications
Module Compiled with Swift 4.0 Cannot Be Imported in Swift 4.0.1
Trigger Local Notifications Automatically Daily on Dynamic Time Given in Arrays Objective C iOS
Rotation Methods Deprecated, Equivalent of 'Didrotatefrominterfaceorientation'
How to Change Inactive Icon/Text Color on Tab Bar
How to Implement Uipageviewcontroller That Utilizes Multiple Viewcontrollers
How to Update a Coredata Entry That Has Already Been Saved in Swift
How to Mask a Square Image into an Image with Round Corners in iOS