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.
iOS Swift MapKit Custom Annotation
I recommend subclassing `MKPointAnnotation.
Pokémon Pin
I have included only the necessary code to display a custom map pin. Think of it as a template.
Outline
We will create a point annotation object and assigning a custom image name with the
CustomPointAnnotation
class.We will subclass the
MKPointAnnotation
to set image and assign it on the delegate protocol methodviewForAnnotation
.We will add an annotation view to the map after setting the coordinate of the point annotation with a title and a subtitle.
We will implement the
viewForAnnotation
method which is anMKMapViewDelegate
protocol method which gets called for pins to display on the map.viewForAnnotation
protocol method is the best place to customise the pin view and assign a custom image to it.We will dequeue and return a reusable annotation for the given identifier and cast the annotation to our custom
CustomPointAnnotation
class in order to access the image name of the pin.We will create a new image set in
Assets.xcassets
and place image@3x.png and image@2x.png accordingly.Don't forget plist.
NSLocationAlwaysUsageDescription
and NSLocationWhenInUseUsageDescription
As always test on a real device.
The swizzle /p>
//1
CustomPointAnnotation.swift
import UIKit
import MapKit
class CustomPointAnnotation: MKPointAnnotation {
var pinCustomImageName:String!
}
//2
ViewController.swift
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
@IBOutlet weak var pokemonMap: MKMapView!
let locationManager = CLLocationManager()
var pointAnnotation:CustomPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!
override func viewDidLoad() {
super.viewDidLoad()
//Mark: - Authorization
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
pokemonMap.delegate = self
pokemonMap.mapType = MKMapType.Standard
pokemonMap.showsUserLocation = true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = CLLocationCoordinate2D(latitude: 35.689949, longitude: 139.697576)
let center = location
let region = MKCoordinateRegionMake(center, MKCoordinateSpan(latitudeDelta: 0.025, longitudeDelta: 0.025))
pokemonMap.setRegion(region, animated: true)
pointAnnotation = CustomPointAnnotation()
pointAnnotation.pinCustomImageName = "Pokemon Pin"
pointAnnotation.coordinate = location
pointAnnotation.title = "POKéSTOP"
pointAnnotation.subtitle = "Pick up some Poké Balls"
pinAnnotationView = MKPinAnnotationView(annotation: pointAnnotation, reuseIdentifier: "pin")
pokemonMap.addAnnotation(pinAnnotationView.annotation!)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print(error.localizedDescription)
}
//MARK: - Custom Annotation
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseIdentifier = "pin"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
let customPointAnnotation = annotation as! CustomPointAnnotation
annotationView?.image = UIImage(named: customPointAnnotation.pinCustomImageName)
return annotationView
}
}
Adding a title in front/below a custom annotation mapkit swift 4
You can create a label like so:
let annotationLabel = UILabel(frame: CGRect(x: -40, y: -35, width: 105, height: 30))
annotationLabel.numberOfLines = 3
annotationLabel.textAlignment = .center
annotationLabel.font = UIFont(name: "Rockwell", size: 10)
// you can customize it anyway you want like any other label
Set the text:
annotationLabel.text = annotation.title!!
And then add to annotation view:
annotationView.addSubview(annotationLabel)
Picture of annotation with label
I also added a background and border by doing:
annotationLabel.backgroundColor = UIColor.white
annotationLabel.layer.cornerRadius = 15
annotationLabel.clipsToBounds = true
You can also change where the label is in respect to the annotation by changing the X and Y when creating the label. Negative is to the left and up, positive right and down.
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)
}
Custom Annotations in SwiftUI (MKMapView)
So basically after a bit more searching, I found out the answer.
In order to not change the user's location pin, I have to check the type of annotation and if the type is MKUserLocation
I should return nil.
Following that the reason for the crash was that I had to make the Stops
structure confirm to MKPointAnnotation
and remove or override the coordinate variable then when I am making a list of Stops
I can simply define the title, subtitle and coordinates.
Related Topics
How to Do Transforms on a Calayer
iOS Keychain Services: Only Specific Values Allowed for Ksecattrgeneric Key
How to Integrate Nsurlconnection with Uiprogressview
Fbsopenapplicationerrordomain Code =4 Error
How to Concatenate Nsattributedstrings
"Could Not Find Any Information for Class Named Viewcontroller"
Swift Framework: Umbrella Header '[...].H' Not Found
How to Hide First Section Header in Uitableview (Grouped Style)
Should I Git Ignore Xcodeproject/Project.Pbxproj File
How to Disable Calayer Implicit Animations
Programmatic Uiscrollview with Autolayout
Uiwebview Random Crash at [Uiviewanimationstate Release]: Message Sent to Deallocated Instance
iOS 11 Uisearchbar in Uinavigationbar
Swift: Testing Optionals for Nil
Remembering Scroll Position on Uitableview
How to Make Uiimageview Automatically Resize to the Size of the Image Loaded