How to Make the scroll of a TableView inside ScrollView behave naturally How to Make the scroll of a TableView inside ScrollView behave naturally swift swift

How to Make the scroll of a TableView inside ScrollView behave naturally


The solution to simultaneously handling the scroll view and the table view revolves around the UIScrollViewDelegate. Therefore, have your view controller conform to that protocol:

class ViewController: UIViewController, UIScrollViewDelegate {

I’ll represent the scroll view and table view as outlets:

@IBOutlet weak var scrollView: UIScrollView!@IBOutlet weak var tableView: UITableView!

We’ll also need to track the height of the scroll view content as well as the screen height. You’ll see why later.

let screenHeight = UIScreen.mainScreen().bounds.heightlet scrollViewContentHeight = 1200 as CGFloat

A little configuration is needed in viewDidLoad::

override func viewDidLoad() {    super.viewDidLoad()    scrollView.contentSize = CGSizeMake(scrollViewContentWidth, scrollViewContentHeight)    scrollView.delegate = self    tableView.delegate = self     scrollView.bounces = false    tableView.bounces = false    tableView.scrollEnabled = false}

where I’ve turned off bouncing to keep things simple. The key settings are the delegates for the scroll view and the table view and having the table view scrolling being turned off at first.

These are necessary so that the scrollViewDidScroll: delegate method can handle reaching the bottom of the scroll view and reaching the top of the table view. Here is that method:

func scrollViewDidScroll(scrollView: UIScrollView) {    let yOffset = scrollView.contentOffset.y    if scrollView == self.scrollView {        if yOffset >= scrollViewContentHeight - screenHeight {            scrollView.scrollEnabled = false            tableView.scrollEnabled = true        }    }    if scrollView == self.tableView {        if yOffset <= 0 {            self.scrollView.scrollEnabled = true            self.tableView.scrollEnabled = false        }    }}

What the delegate method is doing is detecting when the scroll view has reached its bottom. When that has happened the table view can be scrolled. It is also detecting when the table view reaches the top where the scroll view is re-enabled.

I created a GIF to demonstrate the results:

enter image description here


Modified Daniel's answer to make it more efficient and bug free.

@IBOutlet weak var scrollView: UIScrollView!@IBOutlet weak var tableView: UITableView!@IBOutlet weak var tableHeight: NSLayoutConstraint!override func viewDidLoad() {    super.viewDidLoad()    //Set table height to cover entire view    //if navigation bar is not translucent, reduce navigation bar height from view height    tableHeight.constant = self.view.frame.height-64    self.tableView.isScrollEnabled = false    //no need to write following if checked in storyboard    self.scrollView.bounces = false    self.tableView.bounces = true}func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {    return 20}func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {    let label = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30))    label.text = "Section 1"    label.textAlignment = .center    label.backgroundColor = .yellow    return label}func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)    cell.textLabel?.text = "Row: \(indexPath.row+1)"    return cell}func scrollViewDidScroll(_ scrollView: UIScrollView) {    if scrollView == self.scrollView {        tableView.isScrollEnabled = (self.scrollView.contentOffset.y >= 200)    }    if scrollView == self.tableView {        self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)    }}

Complete project can be seen here:https://gitlab.com/vineetks/TableScroll.git


After many trials and errors, this is what worked best for me. The solution has to solve two needs 1) determine who's scrolling property should be used; tableView or scrollView? 2) make sure that the tableView doesn't give authority to the scrollView until it has reached the top of it's table/content.

In order to see if the scrollview should be used for scrolling vs the tableview, i checked to see if the UIView right above my tableview was within frame. If the UIView is within frame, it's safe to say the scrollView should have authority to scroll. If the UIView is not within frame, that means that the tableView is taking up the entire window, and therefor should have authority to scroll.

func scrollViewDidScroll(_ scrollView: UIScrollView) {    if scrollView.bounds.intersects(UIView.frame) == true {     //the UIView is within frame, use the UIScrollView's scrolling.           if tableView.contentOffset.y == 0 {            //tableViews content is at the top of the tableView.        tableView.isUserInteractionEnabled = false       tableView.resignFirstResponder()        print("using scrollView scroll")        } else {            //UIView is in frame, but the tableView still has more content to scroll before resigning its scrolling over to ScrollView.            tableView.isUserInteractionEnabled = true            scrollView.resignFirstResponder()            print("using tableView scroll")        }    } else {        //UIView is not in frame. Use tableViews scroll.        tableView.isUserInteractionEnabled = true       scrollView.resignFirstResponder()        print("using tableView scroll")    }}

hope this helps someone!