Search code examples
swiftsprite-kituipinchgesturerecognizer

How to limit zooming in SpriteKit


I have a Swift + SpriteKit app that loads an SKSpriteNode onto a scene, registers a UIPinchGestureRecognizer, and handles that pinch gesture with a simple handler function, like so:

func zoom(_ sender: UIPinchGestureRecognizer) {

    // Don't let the map get too small or too big:
    if map.frame.width >= 1408 && map.frame.width <= 3072 {
        map.run(SKAction.scale(by: sender.scale, duration: 0))
    }

    print(map.frame.width)
}

However, a pinch will still bring the size of the sprite node to smaller than the specified limits and then when I try to un-pinch again the handler suddenly recognizes the limits I have placed and will not allow and un-pinch gesture.

I have tried doing the same thing with the recognizer's scale attribute:

func zoom(_ sender: UIPinchGestureRecognizer) {

    // Don't let the map get too small or too big:
    if sender.scale >= 0.9 && sender.scale <= 2.1 {
        map.run(SKAction.scale(by: sender.scale, duration: 0))
    }
    print(map.frame.width)
}

but this is even weirder: the sprite node will stop getting smaller with a pinch, but then will grow tremendously massive with an un-pinch.

What is the correct way to put bounds on a pinch gesture?


Solution

  • This is the solution I came up with, but there is one thing I do not like about it:

    func zoom(_ sender: UIPinchGestureRecognizer) {
    
        // If the height of the map is already <= the screen height, abort pinch
        if (sender.scale < 1) {
            if (true) { print("pinch rec scale = \(sender.scale)") }
            if (map.frame.width <= 1408) {
                if (true) { print("Pinch aborted due to map height minimum.") }
                return
            }
        }
    
        // If the height of the map is already >= 2000 the screen height, abort zoom
        if (sender.scale > 1) {
            if (true) { print("pinch rec scale = \(sender.scale)") }
            if (map.frame.width >= 3072) {
                if (true) { print("Pinch aborted due to map height Max.") }
                return;
            }
        }
    
        map.run(SKAction.scale(by: sender.scale, duration: 0))
        sender.scale = 1;
    }
    

    The item that doesn't really work that great is if you perform a fast pinch the map node can decrease to a much smaller size than dictated by the limits in the if statements. If you perform your pinch at a normal speed (not super fast, anyway) then the limits dictated in the if statements are respected.

    I am not sure how to handle that case.