Search code examples
iosswiftselectoruiswipegesturerecognizerpresentviewcontroller

UISwipeGestureRecognizer doesn't work on presented VC and view


The hierarchy:

  • MainVC calls present(GameVC, animated: true, completion: nil)

    let vc = self.storyboard?.instantiateViewController(withIdentifier: "GameVC") as! GameVC self.present(vc, animated: true, completion: nil)

  • GameVC has GameView (subclass of UIView) which covers the whole VC

In the initializer for GameView, I have this code to configure the swipe gestures:

let leftGesture = UISwipeGestureRecognizer(target: self, action: #selector(leftSwipe))
leftGesture.direction = .left
self.addGestureRecognizer(leftGesture)

let rightGesture = UISwipeGestureRecognizer(target: self, action: #selector(rightSwipe))
rightGesture.direction = .right
self.addGestureRecognizer(rightGesture)

let downGesture = UISwipeGestureRecognizer(target: self, action: #selector(downSwipe))
downGesture.direction = .down
self.addGestureRecognizer(downGesture)

Corresponding selectors:

@objc func downSwipe() {
    //code
}

@objc func leftSwipe() {
    //code
}

@objc func rightSwipe() {
    //code
}

The selectors are not getting called. However, when I make GameVC the initial VC that is being displayed(by dragging the storyboard arrow onto GameVC), the gestures work as intended. This makes me think that calling present() might've messed up the hierarchy that gestures operate on, but I'm not quite sure.


Solution

  • You have to indicate in that your gestures can be handled simultanaously by providing corresponding delegate method.

    The below demo code works using above. Tested with Xcode 11.4 / iOS 13.4

    class GameView: UIView {
    }
    
    // in Storyboard just empty view of above custom GameView
    class GameVC: UIViewController, UIGestureRecognizerDelegate {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let leftGesture = UISwipeGestureRecognizer(target: self, action: #selector(leftSwipe))
            leftGesture.direction = .left
            leftGesture.delegate = self
            self.view.addGestureRecognizer(leftGesture)
    
            let rightGesture = UISwipeGestureRecognizer(target: self, action: #selector(rightSwipe))
            rightGesture.direction = .right
            rightGesture.delegate = self
            self.view.addGestureRecognizer(rightGesture)
    
            let downGesture = UISwipeGestureRecognizer(target: self, action: #selector(downSwipe))
            downGesture.direction = .down
            downGesture.delegate = self
            self.view.addGestureRecognizer(downGesture)
    
        }
    
        // allows own view gestures to run with system originated simultaneously
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            true
        }
    
        @objc func downSwipe() {
            print(">> down swipe")
        }
    
        @objc func leftSwipe() {
            print(">> left swipe")
        }
    
        @objc func rightSwipe() {
            print(">> right swipe")
        }
    }
    
    // Initial VC, in storyboard contains only button linked to below showGame action
    class ViewController: UIViewController {
    
        @IBAction func showGame(_ sender: Any) {
            let vc = self.storyboard?.instantiateViewController(withIdentifier: "GameVC") as! GameVC
            self.present(vc, animated: true, completion: nil)
        }
    }