February 24, 2021

Custom UITabelView Footer with dynamic height

By Mohit Agrawal

Design custom Footer view with dynamic height

Hey devs, welcome to another iOS tutorial. Today I will show you how to design a custom view as a Footer view. Suppose you have to display some text in the Footer view but the text comes from the server and you don’t know the length of it. To handle this type of scenario we need to make our footer view completely dynamic using the auto layout.

Now, you know the reason for designing the footer with dynamic height. Let’s jump to the coding part directly.

Let’s code Footer with auto layout

Before any code, let me explain the approach which we are going to use. I will create a custom UIView with a UILabel inside it. This custom view will have the auto layout property.

Once we are ready with a custom view then we will simply assign it to the table view like tableView.tableFooterView = CustomView().

STEP 1: Create a new Xcode project and implement a simple table view as shown below.

Note: I am not going to cover the table view implementation in the tutorial. The main aim of this tutorial is to focus on the Footer view 😬

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    
    let dataArray = [
        "Swift is a powerful and intuitive programming language",
        "Writing Swift code is interactive and fun",
        "Swift includes modern features developers love.",
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.tableFooterView = UIView()
    }
    
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        dataArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = dataArray[indexPath.row]
        return cell
    }

}
dynamic-footer-1

STEP 2: As you can see in the above code and screenshot we have not added any Footer view.

We will now create a new custom view and name it MyFooterView.swift.

import Foundation
import UIKit

final class MyFooterView: UIView {
    
    private let lineSpacing: CGFloat = 4.0
    private let charSpacing: CGFloat = 0.4
    
    lazy var contentLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        label.textColor = UIColor.black
        return label
    }()
    
    init(text: String) {
        super.init(frame: .zero)
        self.contentLabel.text = text
        self.setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        let grayBorder = UIView()
        grayBorder.backgroundColor = UIColor.gray
        grayBorder.translatesAutoresizingMaskIntoConstraints = false
        
        self.addSubview(grayBorder)
        self.addSubview(self.contentLabel)
        
        grayBorder.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor).isActive = true
        grayBorder.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor, constant: 8.0).isActive = true
        grayBorder.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor, constant: -8.0).isActive = true
        grayBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true
        
        self.contentLabel.topAnchor.constraint(equalTo: grayBorder.bottomAnchor, constant: 24).isActive = true
        self.contentLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor, constant: 8.0).isActive = true
        self.contentLabel.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor).isActive = true
        self.contentLabel.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor, constant: -8.0).isActive = true
    }
    
}

The code is self-explanatory, I am just using the programmatic approach to design an auto-layout view. It has one border and one UILabel. You are free to design this view as per your need.

STEP 3: Now set the above custom view as footer view to our table.

override func viewDidLoad() {
        super.viewDidLoad()
        tableView.tableFooterView = MyFooterView(text: "This is the custom footer view, Bingo !!!")
    }

We also need to add one more method which is viewDidLayoutSubviews(). It will help to refresh the layout because table view does not support auto-layout directly.

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        guard let footerView = self.tableView.tableFooterView else {
            return
        }
        let width = self.tableView.bounds.size.width
        let size = footerView.systemLayoutSizeFitting(CGSize(width: width, height: UIView.layoutFittingCompressedSize.height))
        if footerView.frame.size.height != size.height {
            footerView.frame.size.height = size.height
            self.tableView.tableFooterView = footerView
        }
    }

All done, now you can run the project and enjoy a brand new Footer view with no limit 😎.

Code for ViewController.swift

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    
    let dataArray = [
        "Swift is a powerful and intuitive programming language",
        "Writing Swift code is interactive and fun",
        "Swift includes modern features developers love.",
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.tableFooterView = MyFooterView(text: "This is the custom footer view, Bingo !!!")
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        guard let footerView = self.tableView.tableFooterView else {
            return
        }
        let width = self.tableView.bounds.size.width
        let size = footerView.systemLayoutSizeFitting(CGSize(width: width, height: UIView.layoutFittingCompressedSize.height))
        if footerView.frame.size.height != size.height {
            footerView.frame.size.height = size.height
            self.tableView.tableFooterView = footerView
        }
    }
    
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        dataArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = dataArray[indexPath.row]
        return cell
    }

}

Conclusion

I will suggest you design a more complex Footer view based on the above concepts.

Help me to grow our YouTube Channel: More tutorials like this

I hope this blog post is useful for you, do let me know your opinion in the comment section below.
I will be happy to see your comments down below πŸ‘.
Thanks for reading!!!