UITableView header dynamic height in run-time

Viewed 22758

I know there are a lot of posts about it, but maybe in newest iOS there are some updates on this...

I think all of us had a task to create viewController that has a lot of content at the top, most of them are self-sizing, and at the very bottom it figures out that you need to show some tableView with many items...

The first solution that can be done is to use UIScrollView, and don't care about reusableCells at all.
The second is to use UITableView's headerView and adjust its height manually (or by calling systemLayoutSizeFittingSize:) each time when it is needed.
Maybe the third solution is to use UITableView and self-sized UIView separately, with having UIEdgeInsets on tableView. And depending on what object has higher "zIndex", it can bring problems with handling interactions...
The forth solution is to use whole content above the cell, like a separate cell. Not sure this is a good idea at all...

Question: Is there any new solution to this problem? I haven't dig into it for like 2 years... Maybe in new iOS there is something like reusableViews for UIScrollView... Of course, the goal is to have reusable cells, and header with using autolayout without necessity of updating its height manually...

8 Answers

I'm using XCode 10.3 and this is my solution worked with your second solution using table header view.

First, you would create a separating view with xib file, for example with a label inside. And you apply the constraints for this label, top, left, bottom, right to the cell's container view. And set numberOfLines = 0.
Example image
Update your awakeFromNib() function inside your view class.

override func awakeFromNib() {
    super.awakeFromNib()
    ourLabel.translatesAutoresizingMaskIntoConstraints = false
}

Second, on your viewController, setup your tableView:

tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 64

Remember don't delegate this method:

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat

because we set the constraints of our view already.

Finally, you return it on the delegate method

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?

the view for header.

let view = UINib(nibName: String(describing: SimpleHeaderTitleView.self), bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! SimpleHeaderTitleView
 view.ourLabel.text = "Your longgggg text"
 return view



Done! Check it works.

I like the way it's done here:

• If you want to set your tableview header height dynamically based on it's content, just call self.tableView.layoutTableFooterView() right after you have set your headerView as TableViewHeader (so after self.tableView.tableHeaderView = view )

• If you need to update your tableview header height on runtime, also call self.tableView.layoutTableFooterView() right after you have updated the values of your tableview header.

(This obviously also works with tableviewFooters, though, is not to be used for sectionHeaders/Footers)

extension UITableView {

    //Variable-height UITableView tableHeaderView with autolayout
    func layoutTableHeaderView() {

        guard let headerView = self.tableHeaderView else { return }
        headerView.translatesAutoresizingMaskIntoConstraints = false

        let headerWidth = headerView.bounds.size.width
        let temporaryWidthConstraint = headerView.widthAnchor.constraint(equalToConstant: headerWidth)

        headerView.addConstraint(temporaryWidthConstraint)

        headerView.setNeedsLayout()
        headerView.layoutIfNeeded()

        let headerSize = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
        let height = headerSize.height
        var frame = headerView.frame

        frame.size.height = height
        headerView.frame = frame

        self.tableHeaderView = headerView

        headerView.removeConstraint(temporaryWidthConstraint)
        headerView.translatesAutoresizingMaskIntoConstraints = true
    }

}

source

Copied from useyourloaf.com

Swift 5.0

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    guard let headerView = tableView.tableHeaderView else {return}
    let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
    if headerView.frame.size.height != size.height {
        headerView.frame.size.height = size.height
        tableView.tableHeaderView = headerView
        tableView.layoutIfNeeded()
    }
}

Step One :

From interface builder Drag a UIView and drop into

UItableView 

This view will automatically act as a UITableView Header (Mind it not section Header) . Suppose this the width of this view is 200 .If you run the UItableView This view will automatically appear as a Header .

Create a Outlet of this drag-drop View .

@property (weak, nonatomic) IBOutlet UIView *tableViewHeader; // height of this view is 200

Now my goal is increase the height of the table View header .

Step Two :

Add this method

- (void)viewDidLayoutSubviews
{
int increasedHeight = 100;
// set a frame of this view Like example 
self.tableViewHeader.frame = CGRectMake(0, 0, self.view.frame.size.width , 200 + increasedHeight );

self.tableView.tableHeaderView = self.tableViewHeader;

}

Now you tableview header height will be 300 .

If you are using Interface Builder simply you check these buttons enter image description here

You can do that in viewDidLayoutSubviews of the UIViewController that contains your UITableView:

@IBOutlet weak var tableView: UITableView!

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Resize header view with dynamic size in UITableView
    guard let headerView = tableView.tableHeaderView else {
        return
    }
    let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
    if headerView.frame.height != size.height {
        tableView.tableHeaderView?.frame = CGRect(
            origin: headerView.frame.origin,
            size: size
        )
        tableView.layoutIfNeeded()
    }
}

Related