Search code examples
iosxcodeswiftuiswipegesturerecognizer

UIScreenEdgePanGestureRecognizer is triggering multiple times


I'm using the following code for going back and forward in my WebView with UIScreenEdgePanGestureRecognizer:

class FirstViewController: UIViewController {

        @IBOutlet var homewebview: UIWebView!

        // create left edge and right edge gesture
        let leftEdgePanGesture = UIScreenEdgePanGestureRecognizer()
        let rightEdgePanGesture = UIScreenEdgePanGestureRecognizer()


        override func viewDidLoad() {
            super.viewDidLoad()

            // add target for gesture
            self.leftEdgePanGesture.addTarget(self, action: #selector(FirstViewController.handleLeftEdge(_:)))
            self.rightEdgePanGesture.addTarget(self, action: #selector(FirstViewController.handleRightEdge(_:)))

            // set detection edge
            self.leftEdgePanGesture.edges = UIRectEdge.Left
            self.rightEdgePanGesture.edges = UIRectEdge.Right

            // add gesture into view
            self.view.addGestureRecognizer(self.leftEdgePanGesture)
            self.view.addGestureRecognizer(self.rightEdgePanGesture)
        }
        func handleLeftEdge( gesture: UIScreenEdgePanGestureRecognizer ) {
            homewebview.goBack()
        }

        // perform operation when right edge gesture detected
        func handleRightEdge( gesture: UIScreenEdgePanGestureRecognizer ) {
            homewebview.goForward()
        }
    }

My problem is that it's triggering multiple times. It doesn't go back/forward one webpage, it goes back/forward ca. 5 webpages.

Is there any solution? Or will I have to use UISwipeGestureRecognizer. I rather don't want to use UISwipeGestureRecognizer because it doesn't react as reliable as UIScreenEdgePanGestureRecognizer. UISwipeGestureRecognizer sometimes "thinks" I want to scroll vertically in my WebView and doesn't react. Or is there a solution for this?


Solution

  • The pan gesture recognizer calls the handler method any time its state changes. That includes when it starts, ends, and every movement in between. Inside the handler, you need to check the gesture recognizer's state and decide what to do.

    You want to trigger your code only once per gesture, and you want to do it only when the touch has moved more than a threshold amount.

    class ViewController: UIViewController {
    
        let leftEdgePanGesture = UIScreenEdgePanGestureRecognizer()
        var leftPanTriggered: Bool = false
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            leftEdgePanGesture.addTarget(self, action: #selector(handleLeftEdge(_:)))
            leftEdgePanGesture.edges = .Left
            view.addGestureRecognizer(leftEdgePanGesture)
        }
    
        func handleLeftEdge(gesture: UIScreenEdgePanGestureRecognizer) {
            switch gesture.state {
                case .Began, .Changed:
                    if !leftPanTriggered {
                        let threshold: CGFloat = 10  // you decide this
                        let translation = abs(gesture.translationInView(view).x)
                        if translation >= threshold  {
                            performLeftGesture()
                            leftPanTriggered = true
                        }
                    }
    
                case .Ended, .Failed, .Cancelled:
                    leftPanTriggered = false
    
                default: break
            }
        }
    
        func performLeftGesture() {
            homewebview.goBack()
        }
    
    }