Plus Minus One
Published in

Plus Minus One

What if you need to force UITableView to render all of its cells?

There may be times where you need UITableView to display all of its cells. That sounds wrong at first because UITableView's sole purpose is to render only the cells visible to the user for performance considerations. This solution may not be your best option if you have a large number of cells in the TableView you’re about to create. In my situation, TableView consists of 5 to 20 cells and placed beneath a drawing canvas that supports hand drawing. So I could form the feeling of drawing on the entire TableView while scrolling.

Let’s dive into the code.

1 — Start by adding UIScrollView into the ViewController’s view and giving its constraints as follows:

2 — Create a custom UITableViewCell for displaying if you don’t have one yet.

As you will see in the next step, we’re using UITableView.automaticDimension as row height. In order to make automatic dimension work, your cell content (this single label in my case) should have its top and bottom constraints are given to ContentView. This will push the content view to grow its row height up to the point that it will have enough height to show the content.

Don’t forget other configurations for UILabel:

label.numberOfLines = 0

label.lineBreakMode = .byWordWrapping

3 — Programmatically add UITableView as a subview of the previous ScrollView. I will proceed with frame-based layout here.

Important points:

  1. This setup needs TableView's scroll to be disabled otherwise it will result in a conflict with ScrollView's scroll movement and won’t work properly.
  2. TableView needs an initial frame(other than .zero) in order to start rendering cells. This frame is temporary and will be updated on next step.

This is your everyday TableView setup until this point.(except point 1) For example, WillDisplay function in UITableViewDelegate will work as usual.

4 — Now it’s time to adjust TableView’s frame in a way that it will render all the available cells into the ScrollView. Add the following function to your ViewController:

This function wraps reloadData in CATransaction block so the completionBlock will be notified at the moment TableView finishes updating its ContentSize. That is TableView's visible + invisible size. Setting the scrollView's ContentSize to this size and then adjusting the TableView's frame to its ContentSize will force TableViewto draw all cells even if you still see the same visible area. From now on, you should call updateContentHeight() whenever you want to call reloadData to keep the logic working.

Refactor setupTableView() by adding updateContentHeight() at the end:

Now you can use TableView as usual. Most UITableViewDelegate functions will work same, however the ones related with displaying, won’t work as you expected them to do. Two of these functions are as follows:

I provide the next workaround to get the user-visible cell indexPaths.

Set the ViewController as delegate of the scrollView:

Fill the scrollViewDidScroll with the following piece of code. We’re calculating the TableView's visible rect with the help of scrollView's contentOffset. Then calling indexPathsForRows(in: CGRect) will give the indexPaths in that visible rectangle. I’m aware that willDisplay function is called before cells are displayed, however this may still help you.

Here is the result:

You can access the full project here: https://github.com/ozangulhn/ResizedTableView

I would like to hear if you have ideas to improve this logic, so feel free to leave a comment.

At PlusMinusOne we like to learn and share our experiences. We hope this article makes your life easier 🙏🏻

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store