I'm using MapKit and I'm currently trying to set a custom pin annotation image on my pins. However, I'm having a hard time figuring out why it doesn't work. All of the other code is working perfectly, and when I try printing the frame of the image after setting it, it does show the correct dimensions for my "pinImage", so it seems to be able to set the image on the property.
Also, the delegate is set correctly, this is verified by setting a custom color on the default pin.
I've also tried using "pinImage.png", without luck. And since MKPinAnnotationView is a subclass of MKAnnotationView, I see no problem with why that should be the issue, and to be sure, I tried to use the MKAnnotationView also, without luck.
Here's my code:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? Pin {
let identifier = LocalConstants.pinIdentifier
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
let detailButton = UIButton(type: .detailDisclosure) as UIView
view.rightCalloutAccessoryView = detailButton
//view.pinTintColor = Util.Colors.pluppPurple
}
view.image = UIImage(named: "pinImage")
return view
}
Thanks in advance!
The answer was indeed to use MKAnnotationView and not MKPinAnnotationView. I do not know what I messed up when trying it out yesterday. Final working copy below, for future reference:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? Pin {
let identifier = LocalConstants.pinIdentifier
var view: MKAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.tintColor = Util.Colors.pluppGrey
let detailButton = UIButton(type: .detailDisclosure) as UIView
view.rightCalloutAccessoryView = detailButton
}
view.image = UIImage(named: LocalConstants.pluppPin)
return view
}
return nil
}
Related
Hi I'm mostly new to coding and this is my first project that I've worked on. I'm making an app that displays pools which are hiring around your address and wanted to make it so that pressing on the callouts will bring you to a unique ViewController depending on which one you pressed.
From what I've seen so far online it looks like it needs to be done in an extension of the ViewController but was stuck on how to differentiate between callouts (as of right now I have it so that pressing it will show the directions, but I'd rather show more information that just that, which to my limited knowledge would mean displaying another view controller unless anyone else has any other ideas)
extension MapViewController : MKMapViewDelegate
{
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if let annotation = annotation as? Pools {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView =
mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView{
dequeuedView.annotation = annotation
view = dequeuedView
}
else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView
}
return view
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let location = view.annotation as! Pools
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
location.mapItem().openInMaps(launchOptions: launchOptions)
}
}
Any help would be greatly appreciated!
I would like to add a pin on the Map with an image on a gesture.
Actually the add pin on gesture feature works great but without the image because it has to be a MKAnnotationView and not just MKAnnotation.
So I got weird warnings that I'm unable to fix:
override func viewDidLoad() {
super.viewDidLoad()
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
mapView.addAnnotation(annotation)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
let identifier = "CustomAnnotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = false
annotationView?.backgroundColor = UIColor.clear
annotationView!.image = UIImage(named: "map-pinpoint.png")!
} else {
annotationView!.annotation = annotation
}
return annotationView
}
Maybe the errors are because MKPinAnnotationView always use a pin image, so you can't be override. You should use MKAnnotationView instead.
In my app, I have a mapkit full of annotations and when one is clicked I want a new view to slide up with details on the annotation. Correct me if I'm wrong but I think you need to create a custom annotation in order to implement the didSelect() method.
The problem is that, by default, the custom annotations that pop up don't have the name of what the annotations is, like the default mapkit annotations do, and since I have around 20 annotations at a time, the user has no way of knowing what they are selecting.
Any idea as to how to add a title or label underneath the custom annotation with the name of the annotation? I don't want to make a call out that pops up above the annotation since I'm having the view slide up, filled with the annotations data.
Here is what I have:
extension ViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "pin") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView?.animatesDrop = true
annotationView?.canShowCallout = false
} else {
annotationView?.annotation = annotation
}
return annotationView
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
UIView.animate(withDuration: 0.2) {
// the view that will hold the annotations data
self.annotationInfoViewBottomConstraint.constant = 0
self.view.layoutIfNeeded()
}
}
}
As you can see the default annotation has the name of the location underneath, which is what I want but I want it under the "pin" looking custom annotations.
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.
Simple Solution for Name under Marker and color change:
MapKit Delegate Method: (I have used MKMarkerAnnotationView and use markerTintColor to set color)
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "pin") as? MKMarkerAnnotationView
guard let annotation = annotation as? PlaceAnnotation else {
return annotationView
}
if annotationView == nil {
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "pin")
} else {
annotationView?.annotation = annotation
}
annotationView?.markerTintColor = annotation.pinTintColor
return annotationView
}
MKAnnotation Custom Class:
class PlaceAnnotation: NSObject, MKAnnotation {
let title: String?
let locationName: String
let discipline: String
let coordinate: CLLocationCoordinate2D
var pinTintColor: UIColor
init(id: String, title: String, locationName: String, discipline: String, coordinate: CLLocationCoordinate2D, pinTintColor: UIColor) {
self.title = title
self.locationName = locationName
self.discipline = discipline
self.coordinate = coordinate
self.pinTintColor = pinTintColor
super.init()
}
}
How to Use:
let place = PlaceAnnotation(title: "My Location", locationName: "", discipline: "", coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lng), pinTintColor: UIColor.systemBlue)
map.addAnnotation(place)
You'll need to set annotationView.enabled and annotationView.canShowCallout to YES
I have created a map which contains a single annotation. From what I've seen, this is a very simple way of achieving this which I got off a tutorial. I am currently trying to get a custom picture as the annotation but am struggling to do so as almost all information on this topic is in objective C. Forgive me if it is really simple to do but I am relatively new to coding and would appreciate any help :)
class ViewController2: UIViewController {
#IBOutlet var Map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Location for first Pin
let locationOne = CLLocationCoordinate2DMake(-47.016945, 167.852095)
//Location for map centering
let locationNZ = CLLocationCoordinate2DMake(-43.937462, 170.507813)
let span = MKCoordinateSpanMake(9, 9)
let region = MKCoordinateRegion(center: locationNZ, span: span)
Map.setRegion(region, animated: true)
//Create annotation one
let annotation = MKPointAnnotation()
annotation.coordinate = locationOne
annotation.subtitle = "Park"
annotation.title = "Rakiura National Park"
//Add annotation to the map
Map.addAnnotation(annotation)}
I assume its a different image of the default pin you want?
I've used this answer from another question, check it out here
Custom pin image in annotationView in iOS
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
guard !annotation.isKindOfClass(MKUserLocation) else {
return nil
}
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
annotationView = av
}
if let annotationView = annotationView {
// Configure your annotation view here
annotationView.canShowCallout = true
annotationView.image = UIImage(named: "yourImage")
}
return annotationView
}
updating #Jonask'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.
guard !annotation.isKind(of: MKUserLocation.self) else {
return nil
}
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView = av
}
if let annotationView = annotationView {
// Configure your annotation view here
annotationView.canShowCallout = true
annotationView.image = UIImage(named: "yourImage")
}
return annotationView
}
I am creating a map application which allows emojis to be attached to locations. Currently, the image is displayed above the stock apple map annotation.
I wish to hide the stock pin image while still displaying the selected emoji by the user. Is this possible?
I have this code so far to perform this:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
//let identifier = "MyPin"
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
let dictName = arrLocation[((annotation as? MyAnnotation)?.index)!]
let imgName = dictName.valueForKey("pin_img") as? String
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(imgName!)
if annotationView == nil
{
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: imgName)
annotationView!.canShowCallout = false
let name = dictName.valueForKey("name") as! String
let annot = MyAnnotation(titleName:name)
annotationView?.annotation = annot
}
else
{
annotationView!.annotation = annotation
}
let detailButton: UIButton = UIButton(type: UIButtonType.Custom)
// size and placement of emoji on map
detailButton.frame = CGRectMake(-34,-25,85,85)
detailButton.tag = ((annotation as? MyAnnotation)?.index)!
detailButton.addTarget(self, action:"emojiTap:", forControlEvents: UIControlEvents.TouchUpInside)
detailButton.setBackgroundImage(UIImage(named:(dictName.valueForKey("pin_img") as? String)!), forState: UIControlState.Normal)
annotationView!.addSubview(detailButton)
return annotationView
}
Custom annotation view
Instead of using an MKPinAnnotationView, use its superclass MKAnnotationView.
Responding to input
To respond to the user tapping on an annotation implement the mapView:didSelectAnnotation: delegate method. This delegate method is called when the annotation is tapped.
Note that you do not need to use the button inside the annotation view. This means the functionality in emojiTap: must be implemented by the mapView:didSelectAnnotationMethod:.
Example
Combining these
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
let dictName = arrLocation[((annotation as? MyAnnotation)?.index)!]
let imgName = dictName.valueForKey("pin_img") as? String
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(imgName!)
if annotationView == nil
{
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: imgName)
let name = dictName.valueForKey("name") as! String
let annot = MyAnnotation(titleName:name)
annotationView?.annotation = annot
}
else
{
annotationView!.annotation = annotation
}
annotationView!.canShowCallout = false
return annotationView
}
func mapView(_ mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
guard let annotation = view.annotation as? MyAnnotation else {
return
}
print("tapped annotation: \(annotation.index)
// Implement the code from emojiTap: here.
}