Custom table footer views in iOS

This post is the continuation of the adding header views to table view in this post we have learned about how to display custom header view, now we will learn how to show custom footer view with a button action in it.

Now as we did in adding header view same way we will be adding new view that will be registered to table view, in the same project let’s create a new swift file name it as CustomFooterView.swift and add below code.

import UIKit

//1 - Declared Protocol to pass touch event to view controller
protocol FooterViewDelegate: AnyObject {
    func viewMoreButtonTappedInFooter(view: CustomFooterView, inSection section: Int)
}

//2 - Created a class which is sub class of UITableViewHeaderFooterView
class CustomFooterView: UITableViewHeaderFooterView {
    
    //3 - Outlets for border view and button
    @IBOutlet weak var borderView: UIView!
    @IBOutlet weak var viewMoreButton: UIButton!
    
    //4 - Declared deleagte property to set
    weak var delegate: FooterViewDelegate?
    
    //Property to hold the footer
    var footerSection: Int = 0
    
    //4 - After set up and view is ready we are configuring the vie to make bottom left and right to rounded corner
    override func awakeFromNib() {
        super.awakeFromNib()
        self.maskBottomOnlyRoundedCorner(for: self.borderView)
    }
    
    //5 - Helper to make bottom left and right rounded corner
    func maskBottomOnlyRoundedCorner(for view: UIView) {
        view.layer.cornerRadius = 12.0
        view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
    }
    
    //6 - Button action where we are calling the delegate method by passing the footer view itself and footer section to print on the console.
    @IBAction func viewMoreButtonAction(_ sender: Any) {
        self.delegate?.viewMoreButtonTappedInFooter(view: self, inSection: footerSection)
    }
}


//7 - Protocol to ge the reuse ID
extension CustomFooterView: Reuseable {
    static func reuseID() -> String {
        return String(describing: self)
    }
}
  1. We declared a protocol, this will be implemented in view controller, we want our action in view controller because we have whole control over other elements on UI.
  2. Created a class CustomFooterView which is sub class of UITableViewHeaderFooterView.
  3. Declared the outlets borderView and viewMoreButton these are connected to CustomFooterView.xib file soon.
  4. Here awakeFromNib() function get called once we have our xib file loaded and ready to display, we will take this opportunity to configure the borderView to make bottom only rounded corner.
  5. Here we are making the borderView to bottom only rounded corner, because this footer view is appears at the bottom of the seaction.
  6. Action method of UIButton, when user taps on the viewMoreButton this method get called and we are calling the delegate method to handle action in view controller.
  7. Finally our helper method to make clean reuse ID.

Same way we will create a new xib file name it as CustomFooterView.xib and follow the same way as we did for adding the header view, below are the simple things that we did extra compared to adding header view.

Add a view and set its size to free from and border view and inside of border view add a button

After adding the border view set its constrains and add a button set its constraints to appear on right side of the view, and also don’t forget to set its class to CustomFooterView.

Setting up the footer view with buttons and views

Now we have set up our CustomFooterView.xib file and let’s connect the outlets and set title for a for the button as “View More“.

Outlets are connected

Now with the above setup, we will add some code to view controller to configure the footer view, let’s open ViewController.swift file and make changes as below.

private func registerViewsToTableView() {
        tableView.register(UINib(nibName: "CustomTableCell", bundle: nil), forCellReuseIdentifier:CustomTableCell.reuseID())
        
        //Register header view for the table
        tableView.register(UINib(nibName: "CustomHeaderView", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeaderView.reuseID())
        
        //Register header view for the table
        tableView.register(UINib(nibName: "CustomFooterView", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomFooterView.reuseID())
    }

In registerViewsToTableView() method register footer view as we did same for header view.

Now add another delegate method of UITableView called func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? this method simply returns the UIView, also we are adding

func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat delegate method just to return the estimated height of the footer view.

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 5
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return Int.random(in: 3...5)
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: CustomTableCell.reuseID())
        guard let customCell = cell as? CustomTableCell else { return UITableViewCell() }
        return customCell
    }
    
    //Return the header view in this delegate method
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        guard let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: CustomHeaderView.reuseID()) as? CustomHeaderView  else { return nil }
        return headerView
    }
    
    //Setting the height for header view
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 50.0
    }
    
    //Return the footer view in this delegate method
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        guard let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: CustomFooterView.reuseID()) as? CustomFooterView  else { return nil }
        //Make sure to set delegate
        footerView.delegate = self
        footerView.footerSection = section
        return footerView
    }
    
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 50.0
    }
}

In the above delegate method of UITableView we are returning the footer view that we have created above, and also we have setting the delegate property to self and setting the section as well, we will get error for setting the delegate to “self” because ViewController does not confirms to protocol, we can eliminate the error by confirming to protocol and defining the delegate method, add below code in the same ViewController.swift class add below changes.

extension ViewController: FooterViewDelegate {
    func viewMoreButtonTappedInFooter(view: CustomFooterView, inSection section: Int) {
        print("Handle Action for footer in section:\(section)")
    }
}

In the above code we are simply print the message with the section number of the tapped button.

Now its time to run the project and you will see the output like below,

Footer will be displayed at the bottom

Now make to sure you have connected the action for button, if you don’t connect then button action will not get called.

Button action connected

Now after above changes, run the project again and tap on view more button on footer view, you will see console will print the message with the section number.

I have modified the table view with some touch up like removing the separator and setting the table view background color to clear color and setting the view controller’s view background color to gray and the final output like below.

Now we came to end of this tutorial series, at this point you have complete understanding of the customizations of tableview cells, header views and footer view views.

You can download this series sample project from this link.