8

I've got a custom MKAnnotation set up

class Location: NSObject, MKAnnotation {
    var id: Int
    var title: String?
    var name: String

    var coordinate: CLLocationCoordinate2D {
        didSet{
            didChangeValueForKey("coordinate")
        }
    }

when i set the coordinate it does not move the pin on the map. I added the 'didSet' with the key word coordinate as i found people saying that would force it to update. but nothing, it doesn't move. i have verified the lat / long have changed and are correct, the pin just isn't moving. Title updates and displays the change.

i have to assume its because this is a custom annotation. any help?

Jason G
  • 2,395
  • 2
  • 24
  • 34

1 Answers1

19

If you're going to manually post those change events, you have to call willChangeValue(forKey:) in willSet, too. In Swift 4:

var coordinate: CLLocationCoordinate2D {
    willSet {
        willChangeValue(for: \.coordinate)
    }
    didSet {
        didChangeValue(for: \.coordinate)
    }
}

Or in Swift 3:

var coordinate: CLLocationCoordinate2D {
    willSet {
        willChangeValue(forKey: #keyPath(coordinate))  
    }
    didSet {
        didChangeValue(forKey: #keyPath(coordinate))
    }
}

Or, much easier, just specify it as a dynamic property and this notification stuff is done for you.

@objc dynamic var coordinate: CLLocationCoordinate2D

For Swift 2 version, see previous rendition of this answer.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • The need to do both [`willChangeValue`](https://developer.apple.com/documentation/objectivec/nsobject/1416222-willchangevalue) and [`didChangeValue`](https://developer.apple.com/documentation/objectivec/nsobject/1411809-didchangevalue) is fundamental to KVO, which is the mechanism used here. The documentation for both is explicit that each must be used in conjunction with the other. Re `dynamic` and KVO, see [Using Key-Value Observing in Swift: Annotate a Property for Key-Value Observing](https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key_value_observing_in_swift). – Rob Jul 30 '18 at 13:51
  • I know the KVO system, but are you supposed to "know" that it is used in this case and that you need to do it? Is this documented by Apple somewhere in MapKit? – Joris Mans Jul 30 '18 at 14:39
  • I know there is a note buried in the old [Location and Maps Programming Guide](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/AnnotatingMaps/AnnotatingMaps.html#//apple_ref/doc/uid/TP40009497-CH6-SW1), which says: – Rob Jul 30 '18 at 20:19
  • "Important: If you choose to implement the methods for this property yourself, or if you manually modify the variable underlying the property in other parts of your class after the annotation has been added to the map, be sure to send out notifications when you do. Map Kit uses key-value observing (KVO) notifications to detect changes to the `coordinate`, `title`, and `subtitle` properties of your annotations and to make any needed changes to the map display. If you don’t send out KVO notifications, the position of your annotations may not be updated properly on the map." – Rob Jul 30 '18 at 20:19
  • It's also described in the `coordinate` [documentation for `MKAnnotation`](https://developer.apple.com/documentation/mapkit/mkannotation/1429524-coordinate). – Rob Jul 30 '18 at 20:23
  • So if you’re asking how we know we need KVO for these properties, but above references will help. If you’re asking about the general requirement to pair `willChangeValue` with `didChangeValue`, see the `didChangeValue` [documentation](https://developer.apple.com/documentation/objectivec/nsobject/1411809-didchangevalue), which says “Calls to this method are always paired with a matching call to `willChangeValue(forKey:)`." – Rob May 17 '19 at 17:04