2

I have a parent scroll view with a uiview header and table view. I am trying to have a similar behavior to Twitter or Instagram profile page. You start scrolling the parent scroll view and as soon as the header is gone there is a continuous transition from the scroll view scrolling to the table view scrolling without having to lift my finger and replace it on the table view. Right now the current behavior is a little jank. I have to lift my finger and put it on the table view after the scroll view content offset is past the header to start swiping the table view.

MainViewController

    import UIKit
    import XLPagerTabStrip

    class MainViewController: UIViewController {

        lazy var headerViewController: UIViewController = {
            let header = UIViewController()
            return header
        }()

        lazy var bottomViewControllers: BottomPageViewController = {
            let bvc = BottomPageViewController()
            return bvc
        }()

        lazy var scrollView: UIScrollView = {
            let sv = UIScrollView()
            sv.delegate = self
            sv.showsVerticalScrollIndicator = false
            sv.bounces = true
            sv.bounces = false
            return sv
        }()


    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(scrollView)
        let f = UIScreen.main.bounds
        scrollView.frame = CGRect(x: f.minX, y: f.minY, width: f.width, height: f.height)

        add(headerViewController, to: scrollView, frame: CGRect(x: 0, y: 0, width: f.width, height: 150))
        add(bottomViewControllers, to: scrollView, frame: CGRect(x: 0, y: headerViewController.view.bounds.height, width: f.width, height: f.height))
        scrollView.contentSize = CGSize(width: f.width, height: f.height * 2)

        if let vcs = bottomViewControllers.viewControllers as? [BottomViewController] {
            for vc in vcs {
                vc.delegate = self
            }
        }
    }

}

extension MainViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard let controllers = bottomViewControllers.viewControllers as? [BottomViewController] else { return }
        let selectedController = controllers[bottomViewControllers.currentIndex]
        if self.scrollView == scrollView {
            selectedController.tableView.isScrollEnabled = self.scrollView.contentOffset.y >= 150
            print(scrollView.contentOffset.y)
            scrollView.isScrollEnabled = !selectedController.tableView.isScrollEnabled
            bottomViewControllers.view.frame.origin.y = max(150, scrollView.contentOffset.y)
        }


    }
}

extension MainViewController: CustomScrollDelegate {
    func tableViewScroll(_ viewController: BottomViewController) {
        print(viewController.tableView.contentOffset.y)
        viewController.tableView.isScrollEnabled = viewController.tableView.contentOffset.y > 0
    }
}

BottomViewController

import UIKit
import XLPagerTabStrip

protocol CustomScrollDelegate {
    func tableViewScroll(_ viewController: BottomViewController)
}

class BottomViewController: UITableViewController {

    var pageTitle: String?
    var pageIndex: Int = 0
    var delegate: CustomScrollDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .red
        self.tableView.isUserInteractionEnabled = false
        self.tableView.bounces = true
        self.tableView.showsVerticalScrollIndicator = true
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "TETST")
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "TETST", for: indexPath) as? UITableViewCell else { return UITableViewCell() }
        return cell
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1000
    }

    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        delegate?.tableViewScroll(self)
    }

    init(pageTitle: String, pageIndex: Int) {
        self.pageTitle = pageTitle
        self.pageIndex = pageIndex
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

extension BottomViewController: IndicatorInfoProvider {
    func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
        return IndicatorInfo.init(title: pageTitle ?? "Tab \(pageIndex)")
    }
}
joethemow
  • 1,641
  • 4
  • 24
  • 39

1 Answers1

0

You should simply add your header as a header to the UITableView (not a section header!) and let it scroll with the rest of the UITableView: How to scroll the header along with the UITableView?

Also you can make the UITableView link it's contentSize directly to it's height so the UITableView itself doesn't scroll at all, ever, like this: Resizing UITableView to fit content

It all kind of depends if you add rows dynamically by paging etcetera how complex your solution would be. But I think you would get there most of the time by simply setting up the UITableView header in the correct mode

Lucas van Dongen
  • 9,328
  • 7
  • 39
  • 60
  • The problem is that I have 2 table views side by side so I need a separate view for a global header – joethemow Dec 14 '19 at 17:58
  • Take a `UIScrollView`, add an inner `UIView`, add the header at the top and both `UITableView`s below it. Then monitor the `contentSize` of _both_ tableviews and set that as the _minimum_ height of that `UITableView` as a constraint. Now the tallest `UITableView` will always push out the inner `UIView` to the correct height and the shortest `UITableView` will simply get longer without any layout error. Let me know if this works and I'll update the answer. – Lucas van Dongen Dec 15 '19 at 10:44