Expandable and dynamic sized Table Header View and Table Footer View
A quick look at how to dynamically expand and resize a UITableView’s header and footer view.
Images and facts about Luke Skywalker were found at https://starwars.fandom.com/wiki/Luke_Skywalker, and https://swapi.dev/.
UITableView is one of our favourite components when making iOS apps with UIKit. It’s perfect when you need to display a list of content. And it comes with the possibility to add a header and a footer to the top and bottom of your list (UITableView.tableHeaderView and UITableView.tableFooterView). But it has its quirks…
The UITableView has been around since ancient days of iOS, and rely on its header and footer view to manually set their own size (their frame’s height). So even though you are using Auto Layout Constraints to define the internal layout of your UITableView’s header and footer view, you still need to manually set the height of these views. The width will be forced by the UITableView to be equal to the UITableView’s width. This will ensure that the header and footer view have the proper width to fit inside the UITableView. All this makes the UITableView’s header and footer view somewhat cumbersome to work with.
This is all mentioned in the documentation of UITableView.tableHeaderView and UITableView.tableFooterView.
In this article we’ll take a look at how you can dynamically resize your UITableView’s header and footer view without too much of a hassle.
If you just want to get your hands on some example code right away, you can find the complete source code for the example used in this article here: https://github.com/thomsmed/ios-examples/tree/main/DynamicTableHeaderView
Key takeaways
- Manually set the height of your header view and footer view in their layoutSubviews() method (UITableView will force its own width onto them). The UIView.systemLayoutSizeFitting(…) method is great for calculating the height if you are using Auto Layout Constraints. You should make sure to call UITableView.layoutIfNeeded() after you add your header and footer to the UITableView.
- Make layout changes to your header view and footer view inside a call to UITableView.performBatchUpdates(…) to have the UITableView update its layout. This will include the changes made to the header and footer view. UITableView applies a default animation to its UITableViewCells when updating its layout. This animation is not applied to the header and footer view, so you might want to wrap the call to UITableView.performBatchUpdates(…) inside a call to UIView.performWithoutAnimation(…).
- Optionally, if you want to animate layout changes to the header view and footer view along with the UITableView’s default animation, you can apply your own animations with e.g. UIView.animate(…). Just set up your layout changes and animations inside the call to UITableView.performBatchUpdates(…). It might be a good idea to try match the duration of your animations to the duration of the UITableView’s default animation (roughly 0.3 seconds).
Note: You might want to animate the offset of your footer view along any changes to your header’s height, or else the UITableView will just instantly update the footer view’s offset.
An example to go by
As an example, we’ll create a simple application to showcase our favourite Star Wars hero — Luke Skywalker!
The application will have a list of facts about our hero, and an image in the header that expands when tapped on. The footer will hold an expandable horizontal list with information about all the films Luke appears in.
In the remainder of this article, we’ll only look at key parts of what we want to achieve (dynamic sized header view and footer view). So any details about how the app’s UI is constructed is omitted.
You can find the complete source code for the example app used in this article here: https://github.com/thomsmed/ios-examples/tree/main/DynamicTableHeaderView
Images and facts about Luke Skywalker were found at https://starwars.fandom.com/wiki/Luke_Skywalker, and https://swapi.dev/.
Setting the height of the UITableView’s header and footer view
UITableView will force its own width onto its header and footer view, but respect their height, when laying out its subviews (header view and footer view + its UITableViewCells). So the UITableView expect us to manually set the height of its header and footer view.
A neat place to do just that is in the layoutSubviews() method of your header and footer view.
Note: Normally layoutSubviews() is intended to be used to manually set the frames of subviews, and not the view it self. So as an alternative, one could have the parent UITableView / UITableViewController do the resizing at appropriate times.
After adding your header and footer views to the UITableView, you should call UITableView.layoutIfNeeded().
Dynamically resize the UITableView’s header and footer views
When you do layout changes to a UITableView’s header or footer view (typically by changing some Auto Layout Constraints), you need to tell the UITableView about these changes so that it can include them in a layout update. The way to do this is to wrap your layout changes inside a call to UITableView.performBatchUpdates(…).
By default, the UITableView will animate any changes to the position of its UITableViewCells, so if you change the height of the header or footer view, the UITableViewCells will be animated to their new position. The UITableView does not — however — apply this animation to changes done to its header and footer view, so you might want to wrap it all inside a call to UIView.performWithoutAnimation(…). That way none of the layout changes will be animated.
Alternatively, you could just animate changes to the header and footer view along the UITableView’s default animation.
Animate size changes
Changes to your UITableView’s header and footer views can easily be animated by e.g. wrapping them in a call to UIView.animate(…) (inside a call to UITableView.performBatchUpdates(…)).
A good idea is to set the duration of your animation(s) to approximately the same as the UITableView’s default animation (seems to be around 0.3 seconds).
Having both a header view and a footer view
To add and dynamically resize a footer view, you can use the same strategies as for adding and resizing a header view. One tiny thing to notice, though, is that when you animate size changes to the header view that affect the position of the footer view (e.g. shrinking the height of the header view), you might want to animate the footer view’s position change as well (since there is no default animation applied by the UITableView). Otherwise the footer view would just jump straight into its new position.
That’s it!
Hopefully this article has shed some light on how to work with UITableView’s header view and footer view, and provided some useful tips on how to dynamically resize them.
The complete source code for the example app used in this article can be found here: https://github.com/thomsmed/ios-examples/tree/main/DynamicTableHeaderView
Happy coding! 🙌