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
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.
- This setup needs
TableView'sscroll to be disabled otherwise it will result in a conflict with
ScrollView'sscroll movement and won’t work properly.
TableViewneeds 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
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
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.
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.
ViewController as delegate of 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 🙏🏻