Add headers and footers to tableview sections
In the previous post, we saw how to add header and footer views to entire table view. In this post, we will see how to add header and footers to individual UITableView sectionsBefore we get started, make sure (Either programmatically or through storyboard) to add UITableView instance to your main view and pin all the edges to the superView.
Next, we will create the MyHeaderView and MyFooterView to act as header and footer of given TableView section respectively. They both are subclass of UITableViewHeaderFooterView since following method,
tableView.dequeueReusableHeaderFooterView(withIdentifier: <identifier>)
returns the instance which is UITableViewHeaderFooterView subclass.
class MyHeaderView: UITableViewHeaderFooterView {
let label = UILabel(frame: .zero)
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
let customBackgroundView = UIView(frame: .zero)
customBackgroundView.translatesAutoresizingMaskIntoConstraints = false
customBackgroundView.backgroundColor = .green
contentView.addSubview(customBackgroundView)
customBackgroundView.addSubview(label)
label.textColor = .blue
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: customBackgroundView.leadingAnchor, constant: 16.0),
label.trailingAnchor.constraint(equalTo: customBackgroundView.trailingAnchor, constant: -16.0),
label.topAnchor.constraint(equalTo: customBackgroundView.topAnchor),
label.bottomAnchor.constraint(equalTo: customBackgroundView.bottomAnchor),
customBackgroundView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
customBackgroundView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
customBackgroundView.topAnchor.constraint(equalTo: contentView.topAnchor),
customBackgroundView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
])
}
func apply(text: String) {
label.text = text
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Please note following things,
- We are using
customBackgroundViewto color given header or footer view since iOS no longer allow us to directly setbackgroundColorproperty onUITableViewHeaderFooterViewsubclass - We will utilize
reuseIdentifierto save precious memory in case there are many sections and header/footer view may potentially be re-used - We will set the background color of header view to
greenand text toblueto be able to distinguish header from rest of the content MyFooterViewis created the same way. Only distinguishing feature - It's with the backgroundorangeand text color ofblack
Next we register both MyHeaderView and MyFooterView to tableView in our viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyHeaderView.self, forHeaderFooterViewReuseIdentifier: "header")
tableView.register(MyFooterView.self, forHeaderFooterViewReuseIdentifier: "footer")
}
Now assuming you've already registered cells in tableView, implement following dataSource methods to make tableView show some cells and section,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func numberOfSections(in tableView: UITableView) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row \(indexPath.row) Section \(indexPath.section)"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44.0
}
Now is the time to set up UITableViewDelegate methods to properly display header and footer views for each section,
We will implemented following methods to pick heights for header and footer,
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat {
return 44.0
}
Please note that we are using UITableView.automaticDimension for height and value of 44.0 as an estimated height. This is because we want both header and footers to support dynamic sized text where they can grow as text content or size increases.
Now we are entering the final stage. Here we will dequeue cells with identifiers that were previously used to register header and footer view, configure them with arbitrary string and return that view.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header") as? MyHeaderView
header?.configure(text: "Header for Section \(section)\nSomething\nasdasdad")
return header
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: "footer") as? MyFooterView
footer?.configure(text: "Footer for Section \(section)\nSomething\nasdasdad")
return footer
}
I have seen some tutorials where author simply resorts to creating new views inside ofviewForHeaderInSectionorviewForFooterInSectionmethods. This is not an optimal solution. If you've many sections in the tableView, you are essentially creating new view every time to act as a header or footer rather than re-using existing ones
So after all our efforts, our tableView is ready with rows, sections - And most importantly header and footers for each of those sections

And that is all I had to write about header and footer views in TableViews. If you haven't already checked, you can refer to my other post which talks about adding header and footer views to entire tableView.
Questions? Please reach out to me on Twitter!
References:

