Re-registering an AnnotationView - swift

A and B are different kinds of MKMarkerAnnotationView classes. In my MapViewController I register A like this:
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.register(A.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
for annotation in annotations {
mapView.addAnnotation(annotation)
}
}
I have a UISwitch that I want to use to change the registered AnnotationView from A to B.
#IBAction func didToggleSwitch(_ sender: Any) {
mapView.register(B.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
// remove all annotations
mapView.removeAnnotations(mapView.annotations)
//populate all annotations
for annotation in annotations {
mapView.addAnnotation(annotation)
}
}
The registered view isn't being updated. Any ideas why?
Edit: I've noticed that when I zoom out and pan the map around, some annotations update to the new AnnotationView, and some don't. I suspect this is due to using a reuseIdentifier. How do I get all annotations to conform to the new AnnotationView?

I figured it out. I created a boolean within my MKAnnotation class and configured the annotation properties within annotationView A based on that boolean. When I hit the UISwitch, I loop through all my annotation objects, toggle their boolean's value, and re-register the annotationView, and load them up.

Related

Find index of annotation MapKit

I have been trying to performSegue for annotation, but this code generates different indexes and wrong places are shown in detailsView. Would be great to find out how it is possible to fix this.
Thanks
var detailsDict = [NSDictionary]()
var selectedPlace: NSDictionary!
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if (view.annotation?.isKind(of: PlaceAnnotation.self))! {
let annotation = view.annotation
let index = (self.mapView.annotations as NSArray).index(of: annotation!)
let place = detailsDict[index]
selectedPlace = place
performSegue(withIdentifier: "showMuseumDetails", sender: self)
}
}
We can do it using this method
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
let annotation = view.annotation
let index = (self.mapView.annotations as NSArray).index(of: annotation!)
print ("Annotation Index = \(index)")
}
I think the main problem here is that you expect that mapView.annotations returns an array with all annotations being in the same order as your detailsDict-Array. I think that this assumption is error prone, because it would highly depend on the internal implementation of the MapKit framework.
A better way would be storing the Dictionary entry together with the PlaceAnnotation that you add to the map:
Add a new property details to class PlaceAnnotation
Set details value when you create the PlaceAnnotation
in calloutAccessoryControlTapped, get back the details property from the PlaceAnnotation
transfer this value to detail controller (maybe via your selectedPlace property, but I would prefer in instatiating the ViewController directly and setting the value).

Implementing a Delegate Method in a Subclass whose Superclass conforms to that protocol Swift

I have incorporated the GoogleMaps API into my app and all is well with that. However after a bit more development I realized I will need to have two different ViewControllers, both showing a GMSMapView but each having slightly different functionality. I decided to make a base class which has the common functionality and that base class conforms to GMSMapViewDelegate. In this base class, among other things I have:
class BaseMapViewController: UIViewController {
var mapView = GMSMapView()
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
loadMap()
}
func loadMap() {
mapView = GMSMapView(frame: CGRectZero)
mapView.mapType = kGMSTypeHybrid
self.view = mapView
}
}
extension BaseMapViewController: GMSMapViewDelegate {}
One of the subclasses needs to implement func mapView(mapView: GMSMapView!, didTapAtCoordinate coordinate: CLLocationCoordinate2D) so I just implement that in the subclass (shown below), but it doesn't register any taps.
class SellerMapViewController: BaseMapViewController {
override func viewDidLoad(){
super.viewDidLoad()
}
func mapView(mapView: GMSMapView!, didTapAtCoordinate coordinate: CLLocationCoordinate2D) {
print("\(coordinate.latitude,coordinate.longitude)")
}
}
I then tried putting that delegate method in the base class and it still didn't register any taps. Any ideas as to what I am doing wrong or what I could try?
Thanks!
It sounds like there could be a couple things going on:
Make sure you are setting the superclass as the delegate, not just conforming to the protocol.
I have done a similar things in my own code, and what I do is implement a dummy function in the superclass that if called prints a message that its subclass needs to implement it, so that the superclass fully implements the delegate method.
Implement the methods that you need to in your subclass. If the relationship was properly set up in your superclass, the methods will be called in your subclass, overriding the same call in the superclass.

mapView calloutAccessoryControlTapped, how to know who launched it?

I'm working on a project with a map, there are some annotations whose views have a detail button.
Those annotations are created in a for loop where I get data in JSON format, and for every element an annotation is created.
However there's no direct correlation between Annotations (or AnnotationViews) and those elements.
...
anView?.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
...
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
self.performSegueWithIdentifier("openInfo", sender: control)
}
I understand that this button calls the mapView function with calloutAccessoryControlTapped, and passes the AnnotationView.
The problem is that, even if I know the AnnotationView that called the segue, I don't know whose this responds to.
Is there any way to save some informations in the AnnotationView? I mean, so that every instance of AnnotationView has an ID that can relate to the data i received?
Any other idea?
Thanks
You can access the selected annotation from:
let selectedAnnotation = view.annotation as! CustomAnnotation // See structure below.
And if you have a data source which you want to access, it's recommended to create a custom object for your MKAnnotations that contain some reference to your data source, like an identifier. And then set:
selectedId = selectedAnnotation.id
performSegueWithIdentifier("openInfo", sender: control)
And in your prepareForSegue pass the selectedId to the receiving view controller.
EDIT: Your annotation class could look something like this:
class CustomAnnotation: NSObject, MKAnnotation {
var title: String?
var coordinate: CLLocationCoordinate2D
var subtitle: String?
var id: Int
init(title: String, coordinate: CLLocationCoordinate2D, subtitle: String, id: Int) {
self.title = title
self.coordinate = coordinate
self.subtitle = subtitle
self.id = id
}
}

How to change non selected annotation pin images on mapkit with Swift

I have a map and on this map, I have 10 custom annotation pins. All pins have the same custom image. When I click on a pin, I need to change all the other 9 annotation's images. it's possible to change the clicked pin's image but I need to keep as it is and I need to change all other pins images.
I tried to get all annotations with Map mapView.annotations and tried to find selected annotations and change other images but couldn't manage it. And idea how to do it?
Thanks in advance.
Conform to MKMapViewDelegate protocol and then:
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
let selectedAnnotation = view.annotation
for annotation in mapView.annotations {
if let annotation = annotation as? MKAnnotation where !annotation.isEqual(selectedAnnotation) {
// do some actions on non-selected annotations in 'annotation' var
}
}
Also you can save the selected annotation for later use here, if you want to process all annotations in another moment.
finally managed :) solved the problem little bit hard way but working smooth :) thank you for the tip rshev ;)
i used a bool for tap recognize
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is CustomAnnotation {
var pin = mapView.dequeueReusableAnnotationViewWithIdentifier(customAnnotationViewIdentifier)
pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: customAnnotationViewIdentifier)
if tapControl {
pin.image = UIImage(named: "MapAnnotationIcon")
} else {
pin.image = UIImage(named: "SelectedMapAnnotationIcon")
}
if pin == nil {
pin.canShowCallout = false
} else {
pin.annotation = annotation
}
return pin
and when pin tapped ->
if let annotation = view.annotation as? CustomAnnotation {
tapControl = !tapControl
for annotation in mapView.annotations {
if let annotation = annotation as? MKAnnotation where !annotation.isEqual(selectedAnnotation) {
mapView.removeAnnotation(annotation)
}
}
addAnnotations()
println("tapped")
i removed all pins without selected pin, and then draw them back but this time tapcontrol is false so other pins are redrawed with another imageview, so thats what i exactly want to do.
You just have to overrride isSelected property inside your MKAnnotationView subclass.
override var isSelected: Bool {
didSet {
if isSelected {
// do stuff where annotation is selected
} else {
// do opposite
}
}
}

custom MKAnnotationView swift: func mapView does nothing

I am trying to set an image instead of a pin on a map using MapKit. I know I have to set a custom MKAnnotationView. Therefore I should implement the following lines:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "test"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.canShowCallout = true
}
else {
anView.annotation = annotation
}
//Set annotation-specific properties **AFTER**
//the view is dequeued or created...
let cpa = annotation as CustomPointAnnotation
anView.image = UIImage(named:cpa.imageName)
return anView
}
But it is simply not working in my code. I copy/pasted all the code from this post, adding an image called 1.png to my supporting file. But the map displays a red pin instead of the 1.png image.
It looks like the whole mapView function is useless. Do I have to call it in viewDidLoad? How am I supposed to make it being used? Also, what is the reuseId for? Where should I call it?
Thank you for your answers and happy new year.
Be sure that the MKMapView's delegate is properly linked to the ViewController that implements that method.
You could do this in the "viewWillAppear" method like this: mapView.delegate = self;
Or in the InterfaceBuilder:
- Be sure that your MapKit View is selected
- Go to the "Connections Inspector"
- In the "Outlets" section, Drag and drop the "delegate" attribute to the ViewController that implements the delegate method