MapView doesn't show Annotations after adding viewForAnnotations - swift

so I am using MapKit for the first time and I am trying to show annotations with a callout.
The annotations appear but as soon as I add the delegate function viewFor annotation (func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {}) the annotations do not appear anymore. Is there anything I am missing in that function ?
Edit
I forgot to mention that the View Controller is the delegate of MKMapViewDelegate
#objc func addAnnotationOnLongPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == .ended {
let point = gesture.location(in: self.mapView)
let coordinate = self.mapView.convert(point, toCoordinateFrom: self.mapView)
//Now use this coordinate to add annotation on map.
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
usf.fetchJSON(urlString: usf.setNewLocation(lat: annotation.coordinate.latitude, lon: annotation.coordinate.longitude)) { (data, success) in
if success {
annotation.title = data?.name
annotation.subtitle = "HAa"
} else {
let alertUI = self.usf.createAlertOK(title: "Something went wrong!", message: "Cannot get the current weather! \n Check your connectivity or try again later")
self.present(alertUI, animated: true, completion: nil)
}
}
let previousAnnotations = self.mapView.annotations
if !previousAnnotations.isEmpty{
self.mapView.removeAnnotation(previousAnnotations[0])
}
self.mapView.addAnnotation(annotation)
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
print("in viewFor annotation") // prints out
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "TempAnnotationView")
annotationView.canShowCallout = true
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
self.mapView.showAnnotations(mapView.annotations, animated: false)
return annotationView
}

You are instantiating a MKAnnotationView. If you have some custom image, then set the image property of this MKAnnotationView. Or, easier, just use MKPinAnnotationView or MKMarkerAnnotationView instead.
And as Sh_Kahn noted, make sure you create you annotation inside the closure, not after it.

You need
if success {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = data?.name
annotation.subtitle = "HAa"
self.mapView.addAnnotation(annotation)
}
The api that gets name is asynchronous so you have to add the filled annotation inside the callback

Related

Custom MKAnnotationView want to be tapped again

I'm having problems with my custom MKAnnotationView.
I download the coordinate where to place it from my REST api and I place the pin on map with this code:
private func refreshMapView(andCenterMap: Bool){
DispatchQueue.main.async { [weak self] in
guard let self = self else {
return
}
self.mapView.addAnnotations(self.spotsArray)
if andCenterMap {
self.centerMap(at: self.mapView.userLocation.coordinate)
}
}
}
Ones the pin is placed I zoom automatically the map.
Here the code for the custom annotation creation:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Do not touch the annotation of the user location
if annotation.isKind(of: MKUserLocation.self){
return nil
}
let annoIdentifier = "SPOT"
var annotationView: MKAnnotationView?
if let dequeued = mapView.dequeueReusableAnnotationView(withIdentifier: annoIdentifier) {
annotationView = dequeued
}else{
let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annoIdentifier)
annotationView = av
}
// Changing the image of the pin
annotationView!.annotation = annotation
if let image = UIImage(named: "map_pin") {
annotationView!.image = image
let deltaY = image.size.height/2
annotationView!.centerOffset = CGPoint(x: 0.0, y: -deltaY)
}else{
annotationView!.image = nil
}
return annotationView
}
As you can notice, my custom pin is using this image (#1x, #2x, #3x)
Ones tapped I want to show the "detail view" and I use the annotation as a sender in order to have all the information I need on the next view.
Here the code:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let ann = view.annotation else {
return
}
if ann.isKind(of: MKUserLocation.self){
return
}
self.performSegue(withIdentifier: "showDetailSegue", sender: ann)
}
So I present all the data I need to and I can go back to the "map view".
The problem is:
In the map view, if I want to select the same annotation again, this is not tappable anymore.
I can see it (in the right place) but I can select it again only if I zoom a little bit and I tap "around" the annotation.
Any suggestione how to solve this issue?
Thanks in advance!
Add this end of didSelect
mapView.deselectAnnotation(ann,animated:false)

how to add a pin with image when gesture on map

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.

How to draw "rotating" arrow as annotation?

Ok, my new task is this: inside a tracker app I have a map which I'm trying to annotate with custom heading arrow. I need this arrow to be rotated to a certain angle.
I have all the data (i.e. dergrees and locations) and this method:
headingImageView?.transform = CGAffineTransform(rotationAngle: CGFloat(rotationAngle))
So I tried:
Creating an annotation with
let annotation = UserAnnotation(coordinate: locations.last!.coordinate)
mapView.addAnnotation(annotation)
Conforming to MKMapViewDelegate protocol
extension TrackViewController: MKMapViewDelegate
Implementing methods like :viewFor: and also :rendererFor:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if !overlay.isKind(of: MKPolyline.classForCoder()) {
return MKPolylineRenderer(overlay: overlay)
}
let polyline = overlay as! MKPolyline
let renderer = MKPolylineRenderer(polyline: polyline)
renderer.strokeColor = .blue
renderer.lineWidth = 3
return renderer
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is UserAnnotation {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "Online")
annotationView.image = UIImage(named: "arrow")
return annotationView
}
}
And so it kind of works, but I need to rotate an image every time location is updated. How should I do that? Any advice would be appreciated
you can use this code :
but this is important to know you have to put (radian) for rotationAngle
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is UserAnnotation {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "Online")
annotationView.image = UIImage(named: "arrow")
annotationView.transform = CGAffineTransform(rotationAngle: CGFloat(//what you want with type of radian))
return annotationView
}
}
Well, for now I did that:
Globally declared image of an arrow and an optional annotation
let arrow = #imageLiteral(resourceName: "arrow")
var rotatedArrow: UIImage?
var oldAnnotation: MKPointAnnotation?
Rotated image to a certain angle when I get this angle
rotatedArrow = arrow.imageRotated(by: CGFloat(locations.last!.course))
Passed this rotated image to the delegate's method as
annotationView.image = rotatedArrow
return annotationView
Stored and deleted previous annotation
But it all looks soooo ugly

Custom Annotation Swift

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
}

MKAnnotationView Now Showing AccessoryItem

I have tried following different tutorials to make my MKAnnotations have an accessory item that will segue to a different page when clicked; however, I cannot get the item to show. Here is the code I've come up with after reviewing different sources:
extension MapController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "PlaceAnnotation"
if annotation is PlaceAnnotation {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
} else {
annotationView!.annotation = annotation
}
annotationView!.leftCalloutAccessoryView = nil
annotationView!.rightCalloutAccessoryView = UIButton(type: UIButtonType.detailDisclosure)
return annotationView
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let place = view.annotation as! PlaceAnnotation
let placeName = place.title
print(placeName!)
let placeInfo = place.placeObject
let ac = UIAlertController(title: placeInfo?.title, message: placeInfo?.description, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}
}
Then, to create the annotation, I have this:
for place in places {
let placeCoord = CLLocationCoordinate2D(latitude: CLLocationDegrees(place.latitude)!, longitude: CLLocationDegrees(place.longitude)!)
let annotation = PlaceAnnotation(title: place.title, coordinate: placeCoord, placeObject: place)
mapView.addAnnotation(annotation)
}
And the PlaceAnnotation class is as follows:
class PlaceAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var placeObject: Location?
init(title: String, coordinate: CLLocationCoordinate2D, placeObject: Location) {
self.title = title
self.coordinate = coordinate
self.placeObject = placeObject
}
}
The only thing that displays on the annotation when clicked is the Title, but nothing else. I appreciate any help, thank you very much!
(I am working in Swift 3)
I tried your code and it works perfectly fine for me.
But when I removed say,
mapView.delegate = self
Accessory item did not show up, because the delegate is not called.