Since iOS 11, when I use setImage
in my custom MKAnnotationView
, the image is displayed with an animation.
The problem is when I select and deselect the MKAnnotationView
and the image has a different size. It results in a weird animation.
No problem until iOS 11, can we stop this behaviour?
Seeing that this is the only mention of this issue (feature?) that I've found anywhere over the past year and a half, let me first thank you for being my only source of sanity. With that, I've finally been able to circle back and demystify this...sort of. I've yet to file a radar, only because it is unclear whether this behavior is intended. Whatever the correct behavior, the animation has some odd side-effects, and led me to report a separate issue.
To boil it down, the image
field is not an animatable field, but updating it outside of a user-defined transaction triggers a system-performed path animation from the previous image to the new image. Since the animations are performed at the layer-level, UIKit
will not be able to stop them. Instead, we have to halt the current thread transaction's animation actions entirely so that the nested layer animations are never allowed to perform:
CATransaction.begin()
CATransaction.setDisableActions(true)
annotationView.image = newImage
CATransaction.commit()
Rather than cutting off the animation completely, you can also de-jank the path animation by injecting your own properties to the current thread transaction. The catch here, though, is that in order to correctly animate the path & make the appearance that the image isn't moving (ex. animating from a small pin to a large pin), we have to synchronize an additional animation of the centerOffset
. This field does not
directly modify the layer, but it does appear to be a UIKit-animatable field. This means that you need to combine a UIKit animation with a CoreAnimation transaction:
CATransaction.begin()
CATransaction.setAnimationDuration(animationDuration)
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: .easeIn))
UIView.animate(withDuration: animationDuration, delay: 0, options: .curveEaseIn, animations: {
self.image = newImage
self.centerOffset = newCenterOffset
}, completion: nil)
CATransaction.commit()
The duration & timing function must be the same for both the UIKit animation & the CoreAnimation transaction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With