0

I have a tableView.tableHeaderView which is a UITableViewHeaderFooterView.

I would like this header to initially not be displayed when the tableViewController is first presented. When the user scrolls down from the top of the tableView, I would like to present the header.

Then when the user slightly scrolls down, I want the header to snap to a hidden position. The behaviors is exactly like the archived chats header of the WhatsApp page where all your chat's are listed.

Is there any way to achieve this without a complex set of scrollview delegate calls?

I thought on previous versions of swift/xcode, the tableView.tableHeaderView kind of snapped up and down but I notice it's not doing that anymore.

I think the only solution might be overriding the scrollViewDidScroll. This is what I've done but when the tableView.headerView reappears, it does so over the first cell of the tableView. Not sure how to make it appear in the correct position.

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if let myHeaderView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
        let height = (self.navigationController?.navigationBar.frame.size.height)! + UIApplication.shared.statusBarFrame.size.height + self.searchController.searchBar.frame.size.height
        if scrollView.contentOffset.y < -height {
            UIView.animate(withDuration: 0.1) {
                myHeaderView.frame.size.height = 44
            }
        }
    }
}
alionthego
  • 8,508
  • 9
  • 52
  • 125
  • I think your need read this answer [https://stackoverflow.com/a/1086288/3485139](https://stackoverflow.com/a/1086288/3485139) – Maxim Zakopaylov May 22 '18 at 12:42
  • this link is for hiding initially and not snapping. also it doesn't work smoothly for navigationBar's that use largeTitles because the offset you varied will not be taken into account when pushing and popping to another viewController prior to scrolling. This causes some undesirable animations. Looking for a solution to have is snap in and out of place. – alionthego May 22 '18 at 13:19

1 Answers1

0

This is what I've settled on which seems to work pretty well in all cases:

var minimumTableViewInset = CGFloat(88) // this can be adjusted to 32 when the tableView is in landscape.  need observer for rotation to set that.

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.register(UINib(nibName: "MyTableViewHeaderFooterView", bundle: nil), forHeaderFooterViewReuseIdentifier: "MyTableViewHeaderFooterView")

    let listsHeader = tableView.dequeueReusableHeaderFooterView(withIdentifier: "MyTableViewHeaderFooterView") as! MyTableViewHeaderFooterView

    listsHeader.alpha = 0
    listsHeader.frame.size.height = 0
    self.tableView.tableHeaderView = listsHeader
}

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if let headerView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
        let height = (self.navigationController?.navigationBar.frame.size.height)! + UIApplication.shared.statusBarFrame.size.height + self.searchController.searchBar.frame.size.height
        if scrollView.contentOffset.y < -height {
            if headerView.frame.size.height != 44 {
                tableView.beginUpdates()
                headerView.frame.size.height = 44
                tableView.endUpdates()
                UIView.animate(withDuration: 0.5) {
                    self.tableView.tableHeaderView?.alpha = 1
                }
            }
        }
    }
}

override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if let headerView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
        if scrollView.contentOffset.y > -(minimumTableViewInset - CGFloat(10)) {
            if headerView.frame.size.height != 0 {
                tableView.beginUpdates()
                headerView.frame.size.height = 0
                tableView.endUpdates()
                UIView.animate(withDuration: 0.2) {
                    self.tableView.tableHeaderView?.alpha = 0
                }
                tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
            }
        }
    }
}


override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if let headerView = self.tableView.tableHeaderView as? MyTableViewHeaderFooterView {
        if scrollView.contentOffset.y > -(minimumTableViewInset - CGFloat(10)) {
            if headerView.frame.size.height != 0 {
                tableView.beginUpdates()
                headerView.frame.size.height = 0
                tableView.endUpdates()
                UIView.animate(withDuration: 0.2) {
                    self.tableView.tableHeaderView?.alpha = 0
                }
                tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
            }
        }
    }
}
alionthego
  • 8,508
  • 9
  • 52
  • 125
  • make sure the autoresizing is flexibleWidth and FIXED height or you will have all sorts of troubles with header's. That can easily be set in storyboard by clicking the width arrows on and height arrows off, – alionthego May 22 '18 at 14:24