UIKit - Stop ongoing animation on the extended setVisibleMapRect Animation time on MapKit

Hi team, I've been trying to extend the animation when we call the function setVisibleMapRect, we can use UIView.animate to lengthen the animation time, but one thing that I found not working is that when I extend the animation to 3, 5, or 10 seconds, and the changes is still ongoing and there's a gesture performed, the map will completely ignore the gesture. Causing the map to be having this kind of like "delayed" or "freeze" experience for the user. The map will immediately move to the final rect and ignores the user gesture.

I've been checking on this problem for a week now and I'm quite stuck. I've tried using CADisplayLink to manually animate the camera per system fresh rate, it works very well, I can stop the camera movement anytime there are touches, but it causes the resource CPU spikes.

Removing the animation layers recursively on sublayers and subviews also doesn't help. While storing the animation into a UIViewPropertyAnimator and use stopAnimation will always ignores user first interactions too while also animating the camera to the final position (which is not expected).

There is a good example of animating a map using MKMapCamera in our Look Around sample code project. Using the map camera gives you better control over writing your own animation sequences, as the animations provided by setting the animation parameter of setVisibleMapRect to true will be decided by the system. However, as you found, those animations are effectively non-interruptible in practice due to how MapKit manages animations, regardless if you use the UIView family of animation methods, or UIViewPropertyAnimator. Enhancement requests for better integration across the animation APIs are welcomed!

I've tried using CADisplayLink to manually animate the camera per system fresh rate, it works very well, I can stop the camera movement anytime there are touches, but it causes the resource CPU spikes.

While you can use CADisplayLink to control rendering of any UIView on a frame-by-frame basis, you have to keep in mind the stringent performance targets you need to hit in order to not introduce perceivable frame drops. CADisplayLink runs at the display refresh rate, so on devices with a ProMotion display, the display refreshes at 120 Hz, or 120 times every 1,000 milliseconds. That means you have 8.33 ms to render every frame. This can be very difficult to get right, and means you have to be extremely diligent about deferring any non-essential work off the main thread to make sure your main thread can hit those deadlines, so in general, I don't suggest taking this approach due to the difficulty of pulling it off well. I can't comment directly on the CPU spikes directly since it's been some time since I last looked at doing something like this, but I hope your experiences discovering details like that illustrates why this requires detailed engineering diligence to maintain performance.

Removing the animation layers recursively on sublayers and subviews also doesn't help.

In general, you should avoid tinkering with the subviews or sublayers of any views other than your own.

— Ed Ford,  DTS Engineer

Hi Ed,

Thank you for confirming that the native animation APIs are effectively non-interruptible. This validation saves us from chasing a "magic configuration" that doesn't exist.

Sharing a bit about our use cases that the user must be able to interrupt the route preview immediately (e.g., to pan or zoom while the camera is moving on for example, 10 seconds of zooming).

Regarding the performance spikes I mentioned: We have already implemented several optimizations to mitigate the 120Hz/8.33ms constraint you highlighted:

  • Capping Frame Rate: We set preferredFrameRateRange to target 30-45 FPS (avoiding the 120Hz ProMotion tax) to reduce the workload and changed it depends on the device spec, how "close" or "far" is the zooming.

  • Visual Thresholds: We try to skip rendering frames during CADisplayLink ticking with some threshold.

Even with these "throttles" in place, simply calling setVisibleMapRect(..., animated: false) 30 times a second drives CPU usage to 80-100% on the simulator, likely due to the heavy vector tile re-calculations in the MKMapView internals.

Is there a specific "Golden Path" or workaround for interruptible camera movement that we are missing?

For example:

Is there a way to update the camera model without forcing a full immediate tile layout pass every frame?

Alternatively, is there any obscure trick (like a specific RunLoop mode or UIViewPropertyAnimator state manipulation) that can successfully cancel a native setCamera(animated: true) flight without dropping the user's touch?

We are trying to achieve the standard "Navigation Mode" behavior seen in Apple Maps (where the camera moves but the user can grab it instantly), but using public APIs.

We try to figure this out because we benchmark on the Maps app itself when we recenter, we can interrupt the recenter process, as we think this is also can be implemented internally using MapKit

Thanks again for your insight!

UIKit - Stop ongoing animation on the extended setVisibleMapRect Animation time on MapKit
 
 
Q