Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift : Pull down to dismiss `UITableViewController`

I want to pull down to dismiss UITableViewController so I used scrollViewDidScroll method but it didn't works!

    class CommentViewController: PFQueryTableViewController {

        private let tableHeaderHeight: CGFloat = 350.0



extension CommentViewController
{
    override func scrollViewDidScroll(scrollView: UIScrollView)
    {



            // Pull down to dismiss TVC 
            let offsetY = scrollView.contentOffset.y
            let adjustment: CGFloat = 130.0

            // for later use
            if (-offsetY) > (tableHeaderHeight+adjustment) {
                self.dismissViewControllerAnimated(true, completion: nil)
                }
 }
    }
like image 287
marrioa Avatar asked Nov 19 '15 23:11

marrioa


2 Answers

For people looking at this in 2019 -- A more modern approach would use the UIGestureRecognizerDelegate methods, instead of keeping extra state in your view controller. For example:

private weak var panFromTop: UIPanGestureRecognizer?

override func viewDidLoad() {
    super.viewDidLoad()

    // Add pan gesture recognizer
    let panFromTop = UIPanGestureRecognizer(target: self, action: #selector(handlePanFromTop(_:)))
    panFromTop.delegate = self
    tableView.addGestureRecognizer(panFromTop)
    self.panFromTop = panFromTop
}

@objc func handlePanFromTop(_ recognizer: UIPanGestureRecognizer) {
    switch recognizer.state {
    case .began:
        // TODO: BEGIN YOUR ANIMATION HERE
    case .changed:
        // TODO: UPDATE YOUR ANIMATION HERE
    default:
        let translation = recognizer.translation(in: view)
        let velocity = recognizer.velocity(in: view)
        if ((translation.y + velocity.y) / view.bounds.height) > 0.5 {
            // TODO: FINISH YOUR ANIMATION HERE
        } else {
            // TODO: CANCEL YOUR ANIMATION HERE
        }
    }
}

Disable bounce at the top of the table view only:

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y < 0 {
        scrollView.setContentOffset(.zero, animated: false)
    }
}

Then implement the gesture recognizer delegate methods:

func gestureRecognizer(
    _ gestureRecognizer: UIGestureRecognizer,
    shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
) -> Bool {
    return true
}

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    guard let recognizer = gestureRecognizer as? UIPanGestureRecognizer,
        recognizer === panFromTop else {
        // Only require special conditions for the panFromTop gesture recognizer
        return true
    }

    // Require the scroll view to be at the top,
    // and require the pan to start by dragging downward
    return (
        tableView.contentOffset.y <= 0 &&
        recognizer.velocity(in: view).y > 0
    )
}
like image 135
Chris Chute Avatar answered Sep 27 '22 20:09

Chris Chute


You have to implement additional pan gesture recognizer which will recognize simultaneously with scrollView's pan gesture recognizer. Then you can determine whether user is panning by his finger when table view is already scrolled to the top. e.g.

var isTrackingPanLocation = false
var panGestureRecognizer: UIPanGestureRecognizer!

public override func viewDidLoad() {
    super.viewDidLoad()
    tableView.bounces = false
    panGestureRecognizer = UIPanGestureRecognizer(target: self, 
                                                  action: #selector(panRecognized(gestureRecognizer:)))
    panGestureRecognizer.delegate = self
    tableView.addGestureRecognizer(panGestureRecognizer)
}

public func panRecognized(recognizer: UIPanGestureRecognizer) {
    if recognizer.state == .began && tableView.contentOffset.y == 0 {
        recognizer.setTranslation(CGPoint.zero, inView : tableView)

        isTrackingPanLocation = true
    } else if recognizer.state != .ended && 
              recognizer.state != .cancelled && 
              recognizer.state != .failed && 
              isTrackingPanLocation {
        let panOffset = recognizer.translationInView(tableView)

        // determine offset of the pan from the start here. 
        // When offset is far enough from table view top edge - 
        // dismiss your view controller. Additionally you can 
        // determine if pan goes in the wrong direction and 
        // then reset flag isTrackingPanLocation to false

        let eligiblePanOffset = panOffset.y > 200
        if eligiblePanOffset {
            recognizer.enabled = false
            recognizer.enabled = true
            dismissViewControllerAnimated(true, completion: nil)
        }

        if panOffset.y < 0 {
            isTrackingPanLocation = false
        }
    } else {
        isTrackingPanLocation = false
    }
}

public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, 
    shouldRecognizeSimultaneouslyWithGestureRecognizer 
                    otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}
like image 25
Eugene Dudnyk Avatar answered Sep 27 '22 20:09

Eugene Dudnyk