How to add HeaderView and FooterView to UITableView
Recently I was playing with support to add header and footer view to UITableView
. However, task wasn't as easy as adding UITableViewCell
s to the table view. Header and Footer views acts slightly different than regular table view cells. Today we are going to see how to add self-sizing (self-sizing is important) header and footer view to the entire tableView
or individual sections.
We're going to learn following things in today's tutorial,
- Adding Header View to the entire TableView
- Adding Footer View to the entire TableView
Better, we're not just going to add simple header or footer view, but we will make it self-sizing so that they can fit to any content possible
First, let's start with a simple UITableView
in the storyboard, pin it to all edges of its superview and connect IBOutlet
inside the related UIViewController
subclass.
Please also make sure that we're adding UITableViewCell
with the reuse identifier "cell" inside this TableView
- Adding Header View to the entire TableView
Since this is the header view for entire table view, we won't need any kind of reuse identifier since it will appear just once.
Let's start by creating a UIView
subclass with single label which will act as a HeaderView. We expect this UILabel
to be multiline to demonstrate our ability to support self-sizing table view headers.
class Component: UIView {
let label = UILabel(frame: .zero)
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16.0),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16.0),
label.topAnchor.constraint(equalTo: topAnchor),
label.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
func configure(text: String) {
label.text = text
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
We are going to use the same component to act as a Header and Footer view for the same TableView
Next, inside your viewDidLoad
method add following piece of code to instantiate Component
and assign it as a header view
override func viewDidLoad() {
super.viewDidLoad()
let headerView = Component(frame: .zero)
headerView.configure(text: "I am the main header view\nI am second line\nHell yeah")
tableView.tableHeaderView = headerView
tableView.tableHeaderView?.backgroundColor = .red
}
Please note few things,
- We're instantiating
Component
withzero
frame. This is because we don't know how long the label text will grow - We have a
configure
method on the component which allows us to set any arbitrary text to theUILabel
instance - We are coloring
tableHeaderView
withred
background color for easier identification
If we run the app now, we will not see any header.
This is because even though our Component
is set to grow with label, tableHeaderView
associated with tableView
does not support autolayout and will not grow as label grows.
In order to tackle it, we have to wait until tableHeaderView
is fully laid out, grab the systemLayoutSizeFitting
, and get estimated height of label for the given width. We will add utility method to manually adjust the tableHeaderView
height and call it from viewWillLayoutSubviews
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
updateHeaderViewHeight(for: tableView.tableHeaderView)
}
func updateHeaderViewHeight(for header: UIView?) {
guard let header = header else { return }
header.frame.size.height = header.systemLayoutSizeFitting(CGSize(width: view.bounds.width - 32.0, height: 0)).height
}
Please note how we are subtracting 32.0 pixels from available width. This is because the label is provided with padding of 16.0 pixels on both sides
Now if you run the app it will look like this,
2. Adding Footer View to the entire TableView
Now, if you want to add a footer at the bottom, you can follow very similar procedure. Just add the following code in overridden viewDidLoad
method
let footerView = Component(frame: .zero)
footerView.configure(text: "I am the main footer view\nThis is second line\nYes, right")
tableView.tableFooterView = footerView
tableView.tableFooterView?.backgroundColor = .yellow
Now, we also want footerView
to adjust as auto layout lays out rest of the views and update the height from width of the given view so we will just make a call to updateHeaderViewHeight
after viewWillLayoutSubviews
gets called.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
updateHeaderViewHeight(for: tableView.tableHeaderView)
updateHeaderViewHeight(for: tableView.tableFooterView)
}
And we're done with setting flexible height headerView
and footerView
to our tableView
. This is how our completed work looks like,
In the next post we will see how to add header and footer to individual sections in the given TableView
.
Update: New post is done and live. You can refer to it here which talks about adding header and footer views to individual tableView sections