Skip to content

Instantly share code, notes, and snippets.

@smileyborg
Last active February 9, 2024 09:53
Show Gist options
  • Save smileyborg/50de5da1c921b73bbccf7f76b3694f6a to your computer and use it in GitHub Desktop.
Save smileyborg/50de5da1c921b73bbccf7f76b3694f6a to your computer and use it in GitHub Desktop.
How to manually self-size UITableView tableHeaderView/tableFooterView in iOS 11
// For the best results, your tableHeaderView/tableFooterView should be a UITableViewHeaderFooterView with your content inside the contentView.
let tableHeaderView = UITableViewHeaderFooterView()
let fittingSize = CGSize(width: tableView.bounds.width - (tableView.safeAreaInsets.left + tableView.safeAreaInsets.right), height: 0)
let size = tableHeaderView.systemLayoutSizeFitting(fittingSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
tableHeaderView.frame = CGRect(origin: .zero, size: size)
tableView.tableHeaderView = tableHeaderView
// When you set this view to the tableHeaderView/tableFooterView on the table view, the table view will preserve the existing size of its frame.
// If you need to change the size, remove the tableHeaderView/tableFooterView, set a new frame on it, then re-set it on the table view again.
@benpackard
Copy link

benpackard commented May 28, 2019

Should a tableHeaderView really be a subclass of UITableViewHeaderFooterView? The docs say that the UITableViewHeaderFooterView is "a reusable view that you place at the top or bottom of a table section to display additional information for that section." (my bold.) It doesn't mention tableHeaderView or tableFooterView.

@smileyborg
Copy link
Author

@benpackard Yep! Especially if you want the contentView automatically inset to the safe area, e.g. on iPhone X in landscape.

@benpackard
Copy link

benpackard commented May 28, 2019

@smileyborg Thanks! Somewhat confusing given the docs and sometimes the mix up between section headers vs table header view, so I appreciate you confirming.

@gorbat-o
Copy link

Hi, could you explain the context of this piece of code if you have time?

Is it supposed to be called in a viewDidLayoutSubviews for example?

In my case, I am using a container for my headerView and I add the container to the tableheaderView.
It does not seem it will affect what I am doing here :/

For a little bit of context on my side:

// This is a setup function in my viewDidLoad

UIView *headerContainerView = [UIView new];
self.headerView = [CustomView new];

[headerContainerView addSubview:self.headerView];

[self.headerView updateWithTitle:@"Test" andDescription:@"Test description"];
[self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(headerContainerView.mas_top).offset(16);
    make.leading.equalTo(headerContainerView.mas_leading).offset(16);
    make.trailing.equalTo(headerContainerView.mas_trailing).offset(-16);
    make.bottom.equalTo(headerContainerView.mas_bottom).offset(-16);
}];
headerContainerView.transform = self.tableView.transform; // It is legacy code, so the tableview right now is inverted. it should not impact the way we show the tableview.
self.tableView.tableFooterView = headerContainerView;

// The following code was executed before for `tableHeaderView` in the rest of the app, and it was working like a charm. But since I dig a little bit about `tableHeaderViews` apparently set `translatesAutoresizingMaskIntoConstraints ` to `NO` might cause a problem, without even applying any constraints.
//    [headerContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
//        make.centerX.equalTo(self.tableView.mas_centerX);
//        make.width.equalTo(self.tableView.mas_width);
//        make.top.equalTo(self.tableView.mas_top);
//    }];

And then, I have my current ViewController containing this setup that has override viewDidLayoutSubviews:

// It is clearly a workaround for this auto layout issues with tableHeaderView
[super viewDidLayoutSubviews];

if (self.tableView.tableFooterView == nil) {
    return;
}

UIView *tableFooterView = self.tableView.tableFooterView;
CGFloat footerHeight = [tableFooterView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect footerFrame = tableFooterView.frame;

if (footerHeight != footerFrame.size.height) {
    footerFrame.size.height = footerHeight;
    tableFooterView.frame = footerFrame;
    [self.tableView setTableFooterView:tableFooterView];
}

Thank you for your further help and time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment