Search code examples
swiftuigesturerecognizeruipangesturerecognizertranslate-animation

Stop UIPanGestureRecognizer moving past threshold


I have a gesture that is on one of my views. The gesture will not let it go backwards and prevents it from moving past the center of the goal.

If the user drags past the goal to the green side it will stay in the center, but once they begin to drag back towards the button it will retreat. Even if the users finger is not over the XButton view.

code example here:

let xGestureDrag = UIPanGestureRecognizer(target: self, action: #selector(AdViewPageVC.xButtonWasDragged(_:)))

@objc func xButtonWasDragged(_ sender: UIPanGestureRecognizer) {

    if sender.state == UIGestureRecognizerState.began || sender.state == UIGestureRecognizerState.changed {
        self.view.bringSubview(toFront: xButton)
        let translation = sender.translation(in: self.view)
        print(translation.x)
        xButton.center = CGPoint(x: xButton.center.x + translation.x, y: xButton.center.y)
        if xButton.center.x < startingPointxBtn {
            xButton.center.x = startingPointxBtn
        } else if xButton.center.x > largeCircle.center.x {
            xButton.center.x = largeCircle.center.x
        }
        sender.setTranslation(CGPoint.zero, in: self.view)
    } else if sender.state == UIGestureRecognizerState.ended {
        if largeCircle.center.x - xButton.center.x <= 35.0 {
            xButton.center = largeCircle.center
        } else {
            xButton.center.x = startingPointxBtn
        }
    }

}

Video example:

enter image description here


Solution

  • I have implemented an answer. For anybody looking for behavior like this, you can have my solution.

    I used a variable to hold a distance counter of our translations while we slide our view.

    Use this to do some if checks and you get the exact behavior you need.

    video example of new behavior (dragging past goal, behind start, snap to start, and snap to goal):

    enter image description here

    code below:

    @objc func xButtonWasDragged(_ sender: UIPanGestureRecognizer) {
        if sender.state == UIGestureRecognizerState.began || sender.state == UIGestureRecognizerState.changed {
            self.view.bringSubview(toFront: xButton)
            let translation = sender.translation(in: self.view)
            //Move the view
            xButton.center = CGPoint(x: xButton.center.x + translation.x, y: xButton.center.y)
            distanceCounter += translation.x
            //if moved left dont add to our distance counter
            if distanceCounter < 0 {
                distanceCounter = 0
            }
            // prevent button from moving left from start
            if xButton.center.x < startingPointxBtn {
                xButton.center.x = startingPointxBtn
            } else if xButton.center.x > largeCircle.center.x && distanceCounter > xBtnDistance {
                xButton.center.x = largeCircle.center.x
                distanceCounter = xBtnDistance
                // If we hit our target center and go over it in our distance counter
                // make our distance be reached and forced to the center
            } else {
                // set the translation
                sender.setTranslation(CGPoint.zero, in: self.view)
            }
        } else if sender.state == UIGestureRecognizerState.ended {
            // If you let go of button within a distance, snap to center
            if largeCircle.center.x - xButton.center.x <= 35.0 {
                xButton.center = largeCircle.center
            } else {
                // if none of these conditions meet make it snap to start
                xButton.center.x = startingPointxBtn
            }
            // reset our counter to always begin with zero
            distanceCounter = 0
        }
    }