libe
libe

Reputation: 11

How to detect that multiple UIGestureRecognizers end?

in my view I have multiple gesture recognizers (UIPanGestureRecognizer, UIPinchGestureRecognizer and UIRotationGestureRecognizer) and I allowed them to detect touches simultaneously:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                       shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    if gestureRecognizer.view != otherGestureRecognizer.view {
       return false
    }

    return true
}

The problem with this approach is that I am no longer able to detect when all the 3 gestures end. I cannot use

    if gesture.state == .ended || gesture.state == .cancelled {
    }

bacause this is valid only for a gesture recognizer and not for the all 3.

Any idea if there an api to detect when all active recognizers end?

Thanks


Solution

This works but it is very ugly: basically I keep track when all three gestures recognizers end and prevent to detect the end multiple times as the recognizers callback can be called in any order:

class SCCanvasViewController: UIViewController {
    var gesturesAlreadyEnded = false

    lazy var panGestureRecognizer: UIPanGestureRecognizer = {
        let gr = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        gr.delegate = self
        return gr
    }()

    lazy var pinchGestureRecognizer: UIPinchGestureRecognizer = {
        let gr = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
        gr.delegate = self
        return gr
    }()

    lazy var rotateGestureRecognizer: UIRotationGestureRecognizer = {
        let gr = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
        gr.delegate = self
        return gr
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addGestureRecognizer(panGestureRecognizer)
        view.addGestureRecognizer(pinchGestureRecognizer)
        view.addGestureRecognizer(rotateGestureRecognizer)
    }

    func isGestureEnded(gesture: UIGestureRecognizer) -> Bool {
        return gesture.state == .ended || gesture.state == .cancelled || gesture.state == .failed
    }

    func allGesturesEnded() -> Bool {
        let panEnded = isGestureEnded(gesture: panGestureRecognizer)
        let pinchEnded = isGestureEnded(gesture: pinchGestureRecognizer)
        let rotationEnded = isGestureEnded(gesture: rotateGestureRecognizer)
        return panEnded && pinchEnded && rotationEnded
    }

    @objc func handlePan(_ gesture: UIPanGestureRecognizer) {
        if gesture.state == .began {
            gesturesAlreadyEnded = false
        }

        if !gesturesAlreadyEnded && isGestureEnded(gesture: gesture) {
            canvasView.showHorizontalSnapIndicators(areVisible: false)
            canvasView.showVerticalSnapIndicators(areVisible: false)
            if (allGesturesEnded()) {
                gesturesAlreadyEnded = true
                print("Can create transformation command")
            }
            return
        }
    }

    @objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        if gesture.state == .began {
            gesturesAlreadyEnded = false
        }

        if !gesturesAlreadyEnded && isGestureEnded(gesture: gesture) {
            if (allGesturesEnded()) {
                gesturesAlreadyEnded = true
                print("Can create transformation command")
            }
            return
        }
    }

    @objc func handleRotation(_ gesture: UIRotationGestureRecognizer) {
        if gesture.state == .began {
            gesturesAlreadyEnded = false
        }

        if !gesturesAlreadyEnded && isGestureEnded(gesture: gesture) {
            if (allGesturesEnded()) {
                gesturesAlreadyEnded = true
                print("Can create transformation command")
            }
            return
        }
    }
}

extension SCCanvasViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                           shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if gestureRecognizer.view != otherGestureRecognizer.view {
           return false
        }

        return true
    }
}

Upvotes: 0

Views: 964

Answers (1)

clearlight
clearlight

Reputation: 12625

Set it up to get multi-touch notifications, then examine the set of touches impacted by the event.

class TouchableView: UIView {
   var touchViews = [UITouch:TouchSpotView]() 

   override init(frame: CGRect) {
      super.init(frame: frame)
      isMultipleTouchEnabled = true
   }

   required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      isMultipleTouchEnabled = true
   }

   override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
      for touch in touches {
         createViewForTouch(touch: touch)
      }
   }

   override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
      for touch in touches {
         let view = viewForTouch(touch: touch) 
         // Move the view to the new location.
         let newLocation = touch.location(in: self)
         view?.center = newLocation
      }
   }

   override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
      for touch in touches {
         removeViewForTouch(touch: touch)
      }
   }

   override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
      for touch in touches {
         removeViewForTouch(touch: touch)
      }
   }

   // Other methods. . . 
}

Upvotes: 1

Related Questions