50

I'm embedding a UITableView inside of another UIScrollView. I only want the UIScrollView to scroll, not the UITableView, so I want to disable the scrolling in UITableView, as well as expand the contentHeight for the UITableView to accommodate all dynamic at once.

How would I go about doing this?

pableiros
  • 14,932
  • 12
  • 99
  • 105
Allen
  • 3,601
  • 10
  • 40
  • 59
  • Why do you need that feature? You want to add more sub-view in scroll view? – Duyen-Hoa Jun 06 '14 at 20:40
  • 1
    I want to add a basically want to integrate subviews in the same page as a tableview, rather than having the whole view as a tableview. – Allen Jun 06 '14 at 20:42
  • 1
    so you have to expand your tableview to fit the length of that table. then adjust content size of your scroll view – Duyen-Hoa Jun 06 '14 at 20:53
  • After calling `reloadData` the table view should have the correct contentSize if you **don't** implement `estimatedHeight`. – CrimsonChris Jun 06 '14 at 21:03
  • 1
    if other sub-views are on the top of the table view, you can also add these sub-view in tableview: separate by sections. Section 0 contains your subview1, section 1 contains your subview2, section 2 contains your content of the tableview in question. – Duyen-Hoa Jun 06 '14 at 21:42
  • yeah I'm wondering if I should go this route, also has its own complications of having to customize every cell accroding to how I would display the subviews. – Allen Jun 07 '14 at 18:28
  • I'm exactly at that point right now. I started out doing the whole thing inside a single table, but having to deal with multiple sections was not so good. Now my problem is having the table view expanding the height according to the content data... – Nuno Gonçalves Sep 15 '15 at 10:30
  • @Allen Did you get the answer or you have found the solution. If you have got the solution please let us know. I too have the same problem. – G.Abhisek Dec 23 '15 at 13:41

9 Answers9

36

Set tableView scrolling property to false.

First Approach:

  1. Create a subclass for tableView and override intrinsicContentSize.

         class MyOwnTableView: UITableView {
        override var intrinsicContentSize: CGSize {
            self.layoutIfNeeded()
            return self.contentSize
        }
    
        override var contentSize: CGSize {
            didSet{
                self.invalidateIntrinsicContentSize()
            }
        }
    
        override func reloadData() {
            super.reloadData()
            self.invalidateIntrinsicContentSize()
        }
    }
    
  2. In Interface builder change the class of your tableView to MyOwnTableView (subclass UITableView).

  3. Set automatic row height for the table view.

        tableView.estimatedRowHeight = 60.0;
        tableView.rowHeight = UITableViewAutomaticDimension;
    

Second Approach: 1. Create a height constraint with any value for tableView and connect an IBOutlet that sets the tableView height.

  1. Set the height constraint's constant to tableView.contentSize.height after reloading the data.

    self.tableViewHeight.constant = self.tableView.contentSize.height
    
Learner
  • 1,107
  • 12
  • 14
  • 1
    excellent answer, I've seen far to many that only apply to tableViews within viewControllers, along with just plain bad answers with no explanation at all! – James Wolfe Dec 05 '18 at 14:41
  • 1
    @JamesWolfe Thank you! :) – Learner Dec 06 '18 at 16:07
  • First Approach does not work. Second approach works fine. – Softlion Mar 11 '19 at 15:25
  • 1
    Your second approach was perfect for use with Autolayout, thank you! – John Farkerson Aug 13 '19 at 07:46
  • This worked for my use case. I had a tableview inside of a stack view which was inside of a scrollview. I needed the table to expand and show all of its content, and I needed a table because I still wanted swipe actions etc – 72A12F4E Aug 05 '20 at 20:48
12

You need to set the UITableView scroll to false,

tableview.scrollEnabled = false;

tableview.frame = CGRectMake(tableview.frame.origin.x, tableview.frame.origin.y, tableview.frame.size.width, tableview.frame.size.height+(resultArray.count*HEIGHT_OF_CELL));

And then you can add the UITableView on UIScrollView by setting it's content size as UITableView height.

pableiros
  • 14,932
  • 12
  • 99
  • 105
Ahsan
  • 827
  • 6
  • 10
  • 3
    How about dynamic height of cells? – E-Riddie Aug 05 '15 at 14:23
  • 1
    for dynamic height of cell you can calculate the total height in **HeightForRowAtIndexPath** method and then set the scrollView frame accordingly . – sourav Dec 24 '15 at 05:04
  • Ok, but what about lazy loading of cells? If you have 20 cells, they are all loaded at once, not by lazy loading as you scroll. They are not reused... any help? – Slavcho Jun 23 '18 at 12:54
12

Set tableView size equal with contentSize.

override func viewDidLayoutSubviews() {
    tableView.frame.size = tableView.contentSize
}
Huy Le
  • 2,503
  • 1
  • 15
  • 15
10

Basically, tableview's content size is all you need.

  1. Set tableview scrolling to false because we don't want scolling behaviour.

  2. Get the reference of tableview's height constraint.

  3. set tableViewHeightConstraint.constant = tableView.contentSize.height

Make sure, tableview's top, bottom, leading and trailing constraint and are in-place.

Preetam Jadakar
  • 4,479
  • 2
  • 28
  • 58
3
  1. Make your tableview frame cover all lines possible -> no scroll for table view
  2. Set contentSize of your scrollview = size of your tableViews.
Duyen-Hoa
  • 15,384
  • 5
  • 35
  • 44
2
  • First of all you need is to set up the UITableView Scrolling to NO
  • Second, create a height constraint for the table view and after you call [UITableView reloadData] change your height constraint

    tableViewHeightConstraint.constant = numberOfRows * heightOfEachRow;
    
BlaShadow
  • 11,075
  • 7
  • 39
  • 60
1

You could also observe the tableView's contentSize via KVO using Combine.

Idea: You update the height constraint of your tableView, whenever its contentSize changes.

I tried to achieve it using some of the solutions in this thread, but sometimes ended up with wrong contentSize values due to timing issues. Using KVO makes sure to catch all updates on contentSize.

  1. Define a height constraint on your tableView
  2. Make sure to disable scrolling on your tableView
  3. Subscribe to changes on the KVO publisher for the tableView's contentSize in viewDidLoad
  4. Assign the value of the height constraint constant to the value you receive

Code Sample for the KVO part:

class YourViewController: UIViewController {
    private var tokens: Set<AnyCancellable> = []

    func viewDidLoad() {
        super.viewDidLoad()

        tableView.publisher(for: \.contentSize).sink { [weak self] contentSize in
            self?.tableViewHeightConstraint.constant = contentSize.height
        }.store(in: &tokens)
    }
}
bughana
  • 324
  • 5
  • 12
0

UITableView is a scroll view. While you are not supposed to nest UIScrollviews, you can disable scrolling of the the table view.

J2theC
  • 4,412
  • 1
  • 12
  • 14
  • 1
    i understand this, but I will need to adjust the tableview height to accomodate all cells. for instance if I have 5 cells, disabling uitableview scroll would just hide the last 2 of them. – Allen Jun 06 '14 at 20:40
  • You can do that when assigning the UITableViewCell height on the UITableView datasource callback. So, given a number of elements in the data source array, assign a height enough to fit all elements. – J2theC Jun 06 '14 at 20:42
0

While this is very simple to accomplish with a fixed-content tableview as a subview in a scrollview, what is it you are exactly trying to accomplish? Dynamic cells with fixed content above and below? Permanently displayed content with a sliding section in the center?

Perhaps a cleaner design would be using a single UITableView with a Table Header and Footer, or a Section Header an Footer leveraging the automatic behavior provided by UITableViewStyleGrouped vs UITableViewStylePlain

That said, other answers seem to form a decent solution:

Set scroll enabled false on table view
Set tableview content size to sum of all cell sizes
Set tableview frame to content size
Adjust scroll view frame to account for dynamic table size if necessary
Add table view to scroll view amidst other content

Another option for manipulating scroll behavior would be checking the tableview or scrollview in the delegate scrollView shouldStartScrolling or similar methods, as this would allow more flexibility over the UI controls

Lytic
  • 786
  • 5
  • 12