Search code examples
iosswiftuiscrollviewconstraintsuistackview

How to make stackview disappear when scrolling down and re-appear when scrolling up?


I created a stack view with 2 buttons called

Services and Customize

My goal is to make this stack view disappear when I scroll down and make it appear again when I scroll up. Everything works as it should be when I scroll down but when I scroll up, the stack view, I named it

menuStack

it overlaps with the tableview. The tableView is the one that contains images of a shoe as you can see in the screenshot.

I tried this delegate for scrollview but I think something is missing in my anchors. Here are 3 screenshots of each events.

This is the default view

When I scroll down

When I scroll up the menuStack does not reappear

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    self.menuStack.translatesAutoresizingMaskIntoConstraints = false

    if targetContentOffset.pointee.y < scrollView.contentOffset.y {
        //scroll up

        self.menuStack.isHidden = true
        if #available(iOS 11.0, *) {
            let safeGuide = self.view.safeAreaLayoutGuide
            self.menuStack.topAnchor.constraint(equalTo: safeGuide.topAnchor).isActive = true
            self.menuStack.bottomAnchor.constraint(equalTo: self.tableView.topAnchor, constant: 50).isActive = true
            self.tableView.topAnchor.constraint(equalTo: safeGuide.topAnchor, constant: 100).isActive = true
        } else {
            // Fallback on earlier versions
        }


        self.menuStack.isHidden = false

    } else {
        //scroll down
        self.menuStack.isHidden = true
        if #available(iOS 11.0, *) {
            let safeGuide = self.view.safeAreaLayoutGuide
            self.tableView.topAnchor.constraint(equalTo: safeGuide.topAnchor).isActive = true
        } else {
            // Fallback on earlier versions
        }
    }
}

Solution

  • It seems like the scrollViewDidEndDragging function is called when the user lifts their finger off the tableView, this happens before the tableView starts it's deceleration animation. I think you should use scrollViewDidScroll instead. I tried this and it's working for me:

    class ViewController: UIViewController {
        @IBOutlet weak var menuStackView: UIStackView!
        @IBOutlet weak var tableViewTopConstraint: NSLayoutConstraint!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
        }
    
        @IBOutlet weak var tableView: UITableView! {
            didSet {
                tableView.dataSource = self
                tableView.delegate = self
            }
        }
    }
    
    extension ViewController: UITableViewDelegate {
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            menuStackView.isHidden = scrollView.contentOffset.y > CGPoint.zero.y
            tableViewTopConstraint.constant = (scrollView.contentOffset.y > CGPoint.zero.y) ? 0 : 40
    
        //CGPoint.zero.y is when the contentOffset is at the top
        }
    }
    
    extension ViewController: UITableViewDataSource {
        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 100
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
            cell.textLabel?.text = "\(indexPath.row)"
            return cell
        }
    }
    

    I'll post a screenshot of the UI as well so you can see how my design is set up.

    Updated