Why is ViewForAnnotation not called?
Asked Answered
O

3

12

I know this question has been asked multiple times, however all of the answers seem slightly different than what is going on in my app.

My understanding is that the viewForAnnotation function is called once the mapView has its delegate set to the ViewController in which it is being displayed AND an annotation is being added to the map when the mapView scrolls so that it shows inside the mapView region.

Currently I have one main viewController (mainVC) that contains one MKMapView (mapView) This viewController controls four different maps to be displayed in mapView.

func moveViews(sender:Int) {
    // This function handles which button on the Segmented Control was clicked and the loads the appropriate map into the mapView (passed as a para

    removeAnnotationsAndOverlays() // ~~~
    if sender == 0 {
        // First Map was selected
        let map1VC = map1VC()
        map1VC.loadMap1View(mapView)
        map1VC.centerMapOnLocation()
    }
    else if sender == 1 {
        // Second Map was selected
        let map2VC = map2VC()
        map2VC.loadMap2View(mapView)
    }
    else if sender == 2 {
        // Third Map was selected
        let map3VC = map3VC()
        map3VC.loadMap3View(mapView)
    }
    else if sender == 3 {
        // Fourth Map was selected
        let map4VC = map4VC()
        map4VC.loadMap4View(mapView)
    }
    else {
        // Load First Map as default
        let map1VC = map1VC()
        map1VC.loadMap1View(mapView)
        map1VC.centerMapOnLocation()
    }
}

There are several different classes which controls the functionality of each of the different maps:

  1. Map 1 - Displays a combination of MKPolylines and Custom Annotations (inherits from MKAnnotation) that are read from a plist - This works great!
  2. Map 2 - Displays several MKPolylines read from a plist - This works great!
  3. Map 3 - Displays several MKPolylines read from a plist - This works great!
  4. Map 4 - Should display several Custom Annotations - this is NOT WORKING!

Here is what is happening with Map 4:

  • the MKMapView is being loaded properly

    var mapView: MKMapView = MKMapView() // declared as a global variable/object inside the map4VC()
    
    // Initial function to set up Map
    //      Think of this function as the "override func viewDidLoad() {}"
    func loadMap4View(mV: MKMapView) {
    
    // This connects the parameter passed (mV) and sets it as the delegate for the mapView used throughout this file
    //      In other words, it allows the mapView on the MainVC to use all of the code in this file to handle different actions
    mapView = mV
    mapView.delegate = self
    
    let initialLocation = CLLocation(latitude: 50.3603125, longitude: 2.794017)
    
    // calculates the region you'll look at on the screen 
    let coordinateRegion = MKCoordinateRegionMakeWithDistance(initialLocation.coordinate, regionRadius, regionRadius)
    
    // sets the region of the map
    mapView.setRegion(coordinateRegion, animated: true)
    
    
    //addPins()  // This can use a custom function to load all of the Pins
    //mapView.addAnnotations(coords.allLocations!) // This line also works to add all of the individual pins
    
    mapView.addAnnotation(coords.allLocations![2]) // This adds one single Pin
    

    }

  • set the mapView's delegate to the current class (mapView.delegate = self)

  • it zooms in on the proper location (mapView.setRegion(coordinateRegion, animated: true))
  • the class reads from a plist and (using a helper class) builds an array of custom MKAnnotations (CemAnno)

  • The locations are stored in an array of CemAnno's called allLocations:

    var allLocations: [CemAnno]? = [] // This is declared in the helper class along with a bunch of other stuff that grabs all of the information from a plist
    
     class CemAnno: NSObject, MKAnnotation {
    
    var coordinate: CLLocationCoordinate2D
    var title: String?
    var casualties: String?
    var visitInfo: String?
    var histInfo: String?
    var region: String?
    
    
    init(title: String, coordinate: CLLocationCoordinate2D, region: String, casualties: String, visitInfo: String, histInfo: String) {
    
    self.coordinate = coordinate
    self.title = title
    self.region = region
    self.casualties = casualties
    self.visitInfo = visitInfo
    self.histInfo = histInfo
    }
    

    }

    // This builds an object inside the the map4VC() class called coords that holds all of the information collected from the plist
    var coords = BuildCoordinates(filename: "Coordinate")
    
  • adds each one of these to the map (mapView.addAnnotations) and they are displayed as pins (they are being displayed) mapView.addAnnotation(coords.allLocations![2]) // This adds one single Pin Annotation (which works) but never calls the viewForAnnotation function

This works, however, I'm trying to customize the annotations that are being displayed but the ViewForAnnotation function is never called????

// This function is NEVER called
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView?
{
    // Define a reuse identifier. This is a string that will be used to ensure we reuse annotation views as much as possible.
    let identifier = "CemAnno"

    // Check whether the annotation we're creating a view for is one of our CemAnno objects.
    if annotation.isKindOfClass(CemAnno.self) {
        print("correct class")
        //let anno = annotation as! CustomAnnotation
        // Try to dequeue an annotation view from the map view's pool of unused views.
        var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)

        if annotationView == nil {
            print("no reusable view")
            // If it isn't able to find a reusable view, create a new one using MKPinAnnotationView and sets its canShowCallout property to be true. This triggers the popup with the name.
            annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            annotationView!.canShowCallout = true

            // Create a new UIButton using the built-in .Custom type. This is so we can add an image to the button.
            let btn = UIButton(type: .DetailDisclosure)
            //btn.setImage(anno.image, forState: .Normal)
            annotationView!.rightCalloutAccessoryView = btn
        } else {
            print("reusing a view")
            // If it can reuse a view, update that view to use a different annotation.
            annotationView!.annotation = annotation
        }

        return annotationView

    }

    // If the annotation isn't from a CustomClass, it must return nil so iOS uses a default view.
    return MKPinAnnotationView()
}

I've tried adding the location of a pin inside the current view region of the map but the ViewForAnnotation is never fired. I've tried adding the location of the pin outside of the current view region of the map but the ViewForAnnotation is never fired - I figured this should be the one that works, and as I 'scroll' the map and it shows up inside the current view region that should fire the function, but it doesn't (I should note that the pin is being displayed and shows up on the map).

This makes it so that I cannot customize the pin which I would like to do.

I'm using the same technique as Map 1 which works perfectly fine, but for some reason the ViewForAnnotation is never called inside Map 4.

Any suggestions would be greatly appreciated!!

Oospore answered 27/3, 2016 at 17:58 Comment(6)
some code might helpJoyann
I've added code. Now I notice as I change from Map1 to Map4 back to Map1 only some of the time are my custom Annotations showing up with the images I want. Other times they are only being displayed with a pin.Oospore
I've played around with multiple things and I think it must have something to do with the delegate. Although the delegate is being set it is as if it isn't set properly to the instance of the MKMapView that is being displayed (despite being set in the first line of the func loadMap4View(mV: MKMapView) method). Could it have something to do with the mV parameter being passed to a separate class (passed by reference or type)??? Any ideas?Oospore
Weird Functionality: On the MainVC the different Maps are selected by using a Segmented Control. As I quickly press on different buttons to load the different maps, roughly about 10% of the time the program breaks and hits the breakpoint inside of the ViewForAnnotation method (which is what I want to happen). Why only 10% of the time? I can't replicate it under certain circumstances, it seems entirely random.Oospore
If you are adding annotations in AsyncQueue plz add annotations in mainQueue. #3822015Collector
How do you know whether you are adding annotations in AsynQueue? Is that where annotations are added by default?Oospore
D
14

I encountered this problem from time to time.And I found that sometimes you need to call showAnnotations:animated: manually like this :

mapView.showAnnotations(mapView.annotations, animated: true)
Dilatation answered 4/4, 2016 at 14:20 Comment(3)
It's working slightly better but is still fairly glitchy. The ViewForAnnotation is fired almost at random and sometimes the images result instead of the pin annotation, and other times not so much.Oospore
@M.Black The problem may locate in 'mapView = mV' .A more appropriate way to do that would be :1. remove old mapview,2.set new mapview's frame same as old mapview,3.add new mapviewDilatation
This is the closest solution to working although it is still seemingly calling the ViewForAnnotation method at random and loading the appropriate images only some of the time. Sorry wj2061 that the bounty wasn't awarded to you (I'm not sure why it was awarded to the other solution???)Oospore
C
9

Sorry but I can't see the declaration of your view controller mainVC: your controller implements the MKMapViewDelegate?

import MapKit

class mainVC: UIViewController, MKMapViewDelegate 
{
    ...
} 

EDIT 1: check also if in your view controller on your storyboard if the delegate is linked with your controller like this:

just right click on your mapView.

Concertina answered 4/4, 2016 at 9:49 Comment(2)
Yes it does. So do all of the other classesOospore
The delegate is set in code (as was indicated above). I'm surprised this solution has received upvotes considering it doesn't explain why it works some of the time and not others. There are four different classes that are handling loading the mapView on the MainVC. So, I DON'T want the delegate to be the main view controller because the code is contained in other view controller classes for each of the individual maps.Oospore
M
3

This is an incredibly dumb answer, but it's bit me twice today so here it is: check your latitude/longitude. If you have them swapped, you have an invalid point, or one in the middle of the ocean, so the view will never be requested as it is likely outside your visible rect.

Mychael answered 15/8, 2018 at 23:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.