Reputation: 14380
I have an MKMapView
with 2 buttons on it: zoom in and zoom out.
I noticed that when I use them, I can't pinch the map to zoom anymore until the animation is done.
I have my buttons hooked up to setRegion
on a smaller or larger span than it is now.
I tried adding a UIPinchGestureRecognizer
to my map to stop the animation and allow the pinch to work. Here is how I did that:
I added a Bool
variable that keeps whether the map is currently animating from a tap on the buttons.
func pinchRecognized() {
if animating {
var region = self.region
region.span.latitudeDelta += 0.001
setRegion(region, animated: false)
animating = false
}
}
I override setRegion like this:
override func setRegion(_ region: MKCoordinateRegion, animated: Bool) {
if (animated)
{
animating = true
super.setRegion(region, animated: animated)
perform(#selector(noLongerAnimating), with: nil, afterDelay: 1)
}
else
{
super.setRegion(region, animated: animated)
}
}
func noLongerAnimating() {
animating = false
}
These work in stopping the animation, but the pinch is not recognised by the map itself to zoom, even though I do this:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
I guess the setRegion
in pinchRecognized
breaks it, but I don't know how else to stop the animation.
As requested, the button code, both buttons use this method, zoom in uses 0.5, zoom out uses 2:
func zoomTo(delta: Double, animated: Bool) {
var region = self.region
var span = region.span
span.latitudeDelta *= delta
span.longitudeDelta *= delta
if (span.latitudeDelta < 180 && span.longitudeDelta < 180)
{
region.span = span
setRegion(region, animated: animated)
}
}
Edit: I tried setting the setRegion
(the one that stops the animation) in gestureRecognizer:shouldRecognizeSimultaneouslyWith:
, but there it doesn't get called while animating the map.
Edit: After trying what @robinb suggested, I saw that my annotations update quicker than my map itself, suggesting that the region gets set, it just waits for something to visually update the map.
Upvotes: 18
Views: 888
Reputation: 3433
MapView animations set isUserInteractionEnabled to false upon start, However if you wanna force that, you can subclass MKMapView and override UIView's touchesBegan method to respond to users interaction. There you can first either call setCenter/setRegion (either the beginning or destination values) without animation to interrupt/quit the animation in progress and set isUserInteractionEnabled to true.
MKMapView uses CATiledLayer and unfortunately there is no easy way to get the region and or centre of the map at the time of interruption. On the other hand, I noticed the animation duration is constant though private so you can use Timer to make a better guess of the map's region when touchesBegun is called
private var isAnimating: Bool = false
override func setRegion(_ region: MKCoordinateRegion, animated: Bool) {
isAnimating = animated
...
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isAnimating {
isAnimating = false
let region = // Destination region or the approximate region where the animation reached at this moment (although this is a nightmare todo)
setRegion(region: region, animated: false) // or use setCenter
isUserInteractionEnabled = true
}
}
Upvotes: 1
Reputation: 1101
Gesture recognizers don't fire on views that are being animated. Hold the views in a variable/array at the ViewController. Use the code in this post to execute the animations: https://stackoverflow.com/a/13814789/3970581
Here is the testproject: https://github.com/DuncanMC/iOS-CAAnimation-group-demo
Upvotes: 2