iosswiftuitableviewtableviewswift5

iOS - Show header view in 2 tableviews next to each other


I have a view controller that shows two tableViews side by side.

I have also a class (ButtonHeaderFooterView) that extends from UITableViewHeaderFooterView.

I want to show different titles for the header depending on the table view, but it's not working for me.

ViewController.swift

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet private var leftTableView: UITableView!
    @IBOutlet private var rightTableView: UITableView!
    
    private var cachedHeader: ButtonHeaderFooterView?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        ButtonHeaderFooterView.register(forTable: leftTableView)
        ButtonHeaderFooterView.register(forTable: rightTableView)
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return UITableViewCell()
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if tableView == leftTableView {
            return ""
        }
        return ""
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        if let cachedHeader = cachedHeader {
            return cachedHeader
        }
        guard let headerView = ButtonHeaderFooterView.dequeue(forTable: tableView) else {
            fatalError("Failed to dequeue header/footer view buttonHeaderFooterView")
        }
        cachedHeader = headerView
        return headerView
    }
    
    func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        guard let header = view as? ButtonHeaderFooterView else {
            return
        }
        if tableView == leftTableView {
            header.textLabel?.text = "Test 1"
        } else {
            header.textLabel?.text = "Test 2"
        }
        
        
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 36
    }
}

ButtonHeaderFooterView.swift

import Foundation
import UIKit

protocol ButtonHeaderFooterDelegate: AnyObject {
    func buttonTapped()
}

class ButtonHeaderFooterView: UITableViewHeaderFooterView {
    static let identifier = "ButtonHeaderFooterView"

    @IBOutlet private var titleLabel: UILabel!

    static func dequeue(forTable tableView: UITableView) -> ButtonHeaderFooterView? {
        tableView.dequeueReusableHeaderFooterView(withIdentifier: ButtonHeaderFooterView.identifier) as? ButtonHeaderFooterView
    }

    static func register(forTable tableView: UITableView) {
        tableView.register(UINib(nibName: "ButtonHeaderFooterView", bundle: nil), forHeaderFooterViewReuseIdentifier: ButtonHeaderFooterView.identifier)
    }
}

Here's a screenshot of what I see.

enter image description here

Do you know what I'm doing wrong?


Solution

  • I think the problem may be related to the cachedHeader ivar. I think it needs to be removed along side with:

    You can set the string right after dequeuing the header view.

    Probably something along the lines of will be a better bet:

    import UIKit
    
    class ViewController: UIViewController {
        
        @IBOutlet private var leftTableView: UITableView!
        @IBOutlet private var rightTableView: UITableView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            ButtonHeaderFooterView.register(forTable: leftTableView)
            ButtonHeaderFooterView.register(forTable: rightTableView)
        }
    }
    
    extension ViewController: UITableViewDataSource, UITableViewDelegate {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 2
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            return UITableViewCell()
        }
        
        func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
            guard let headerView = ButtonHeaderFooterView.dequeue(forTable: tableView) else {
                fatalError("Failed to dequeue header/footer view buttonHeaderFooterView")
            }
    
            if tableView == leftTableView {
                headerView.textLabel?.text = "Test 1"
            } else {
                headerView.textLabel?.text = "Test 2"
            }
    
            return headerView
        }
            
        func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
            return 36
        }
    }
    

    There is not need to cache the header view as the table view will do it for you. This is why you need to dequeue the header view in the first place.