My Swift Tool Belt (Part 4): Extending UITableViewController

tool-belt

The fourth item in my Swift Tool Belt is an extension for UITableViewController. A UITableViewController is a view controller with a table view that takes up the entire screen. My extension provides a couple of methods to resize the header and footer of a table view.

The Problem

The table view has a header and footer which allows you to put UI controls above and below the data in your table view. The problem is that the header and footer do not participate with Auto Layout.

The height of the header and footer need to be set manually. You can set an initial size for your header in Xcode, but that may not work for all phone sizes or situations where you have dynamic content in your header that you hide/show at runtime.

To demonstrate the problem, I’ve added a header view to my table view. Inside the header view, I’ve added a stack panel with four labels in it. As you can see, when I run the project, all four of my labels do not appear in the header.

The Extension to Solve the Problem

This extension will resize the header or footer to the proper size based on the Auto Layout constraints.


import UIKit

extension UITableViewController {
    func sizeHeaderToFit() {
        if let headerView = tableView.tableHeaderView {
            
            headerView.setNeedsLayout()
            headerView.layoutIfNeeded()
            
            let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
            var frame = headerView.frame
            frame.size.height = height
            headerView.frame = frame
            
            tableView.tableHeaderView = headerView
        }
    }
    
    func sizeFooterToFit() {
        if let footerView = tableView.tableFooterView {
            footerView.setNeedsLayout()
            footerView.layoutIfNeeded()
            
            let height = footerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
            var frame = footerView.frame
            frame.size.height = height
            footerView.frame = frame
            
            tableView.tableFooterView = footerView
        }
    }
}

You can call these functions when your header or footer view content has changed. I usually call them once when my table view controller has initially loaded to set the initial height, then again when the content has changed.

To fix my previous example, I call sizeHeaderToFit in viewDidLoad to set the initial height of my header.


override func viewDidLoad() {
    super.viewDidLoad()
    sizeHeaderToFit()
}

When I run my app, you can see the header resizes appropriately to fit all of the content.

I use this extension anytime I have a header and footer in my table view. Hopefully, you find it useful as well.


My Swift Tool Belt

Catch up on the other posts in this series: