4

I don't understand how to change the image of a mark point in Swift. I have tried a few variations of

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! 

but it seems to never get called and I don't know how to force it to be called.

I will say I added the MKMapView programmatically too so I can switch sizes (horizontal and vertical) based on which iPhone is being used. (I still can't believe this isn't intuitive to Xcode and the programmer has to program around it.)

Here is my code:

@IBAction func addStroke(sender: UIButton) {
    NSLog("Add Stroke Touched")
    NSLog("X %f Y %f", manager.location.coordinate.latitude, manager.location.coordinate.longitude)
    GPSspot = manager.location.coordinate

    annotation.setCoordinate(GPSspot)
    annotation.title = "Stroke"

    let useID = "test"

    var myAnnotation = mapHole.dequeueReusableAnnotationViewWithIdentifier(useID)
    if (myAnnotation == nil) {
        myAnnotation = MKAnnotationView(annotation: annotation, reuseIdentifier: useID)
        myAnnotation.image = UIImage(named: "ballSmall.png")
        myAnnotation.canShowCallout = true
        myAnnotation.enabled = true
    } else {
        myAnnotation.annotation = annotation
    }

    mapHole.viewForAnnotation(annotation)
    mapHole.addAnnotation(annotation)
}

//MARK: - Map Kit
var mapRect = MMRect(xLoc: 502, yLoc:20, yWidth:218, xHeight:386)
var mapHole:MKMapView! //= MKMapView() as MKMapView
var annotation = MKPointAnnotation()
var MAView:MKAnnotationView!

//MARK: - GPS Locations
var manager:CLLocationManager!
var GPSspot:CLLocationCoordinate2D!
var span:MKCoordinateSpan!
var region:MKCoordinateRegion!

//MARK: - Default Initializer
override func viewDidLoad() {
    super.viewDidLoad()

    //MARK: Locations
    manager = CLLocationManager()
    if (CLLocationManager.locationServicesEnabled()) {
        NSLog("locations services started")
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.requestAlwaysAuthorization()
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    } else {
        NSLog("location services failed")
    }


    //MARK: MKMapView
    mapHole = MKMapView(frame: mapRect.textRect)
    span = MKCoordinateSpanMake(0.001, 0.001)
    GPSspot = manager.location.coordinate
    region = MKCoordinateRegion(center: GPSspot, span: span)
    mapHole.setRegion(region, animated: true)
    mapHole.mapType = MKMapType.Satellite
    mapHole.showsUserLocation = true
    self.view.addSubview(mapHole)
}

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    if annotation is MKUserLocation {
        return nil
    }

    let reuseId = "test"

    var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
    if anView == nil {
        anView = MKAnnotationView (annotation: annotation, reuseIdentifier: reuseId)
        anView.image = UIImage(named:"ballSmall.png")
        anView.canShowCallout = true
    } else {
        anView.annotation = annotation
    }

    return anView
}

Can anyone help? Thank you! I have been working on this and reading for about a week now and I just don't get it.

Scuttle
  • 165
  • 3
  • 11

1 Answers1

2

The viewForAnnotation method never gets called because the map view's delegate is not set when the map view is created programmatically in viewDidLoad.

After creating the map view, set its delegate property:

mapHole = MKMapView(frame: mapRect.textRect)
mapHole.delegate = self                       // <-- add this line
span = MKCoordinateSpanMake(0.001, 0.001)

Additionally, if you haven't already, you must declare that the view controller implements the MKMapViewDelegate protocol. For example:

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

Note that setting the delegate property on the map view must be done as well as declaring that the view controller implements the protocol.


You should also remove the view related code from the addStroke method since it doesn't belong there, has no effect, and is pointless:

annotation.title = "Stroke"

/* This code doesn't belong here...
let useID = "test"

var myAnnotation = mapHole.dequeueReusableAnnotationViewWithIdentifier(useID)
if (myAnnotation == nil) {
    myAnnotation = MKAnnotationView(annotation: annotation, reuseIdentifier: useID)
    myAnnotation.image = UIImage(named: "ballSmall.png")
    myAnnotation.canShowCallout = true
    myAnnotation.enabled = true
} else {
    myAnnotation.annotation = annotation
}

mapHole.viewForAnnotation(annotation)
*/

mapHole.addAnnotation(annotation)


Also note this code in viewDidLoad:

GPSspot = manager.location.coordinate
region = MKCoordinateRegion(center: GPSspot, span: span)
mapHole.setRegion(region, animated: true)

can potentially crash because manager.location may still be nil at this point so soon after calling startUpdatingLocation. It would be safer either to first check if manager.location is not nil or move that code to the didUpdateLocations delegate method.


Finally, regarding:

...I added the MKMapView programmatically to so I can switch sizes (horizontal and vertical) based on which iPhone is being used. (I still can't believe this isn't intuitive to Xcode and the programmer has to program around it.)

Note that Xcode is just the IDE to let you write and compile the code. It's the iOS SDK that would know about which iPhone is being used. The SDK can automatically resize views based on screen size. Search for "Auto Layout" and/or "Size Classes" in the documentation, WWDC videos, SO, or other resources.

  • THANK YOU!!!!! I missed adding the delegate and now it works. THANK YOU! (Now on to the next week long problem to solve.) – Scuttle Oct 18 '14 at 03:42