table header view height is wrong when using auto layout, IB, and font sizes
Solution 1
Note: A Swift 3+ version can be found here: https://gist.github.com/marcoarment/1105553afba6b4900c10#gistcomment-1933639
The idea is to calculate header's height with help of systemLayoutSizeFittingSize:targetSize
.
Returns the size of the view that satisfies the constraints it holds. Determines the best size of the view considering all constraints it holds and those of its subviews.
After changing header's height it is necessary to reassign tableHeaderView
property to adjust table cells.
Based on this answer: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
- (void)sizeHeaderToFit
{
UIView *header = self.tableView.tableHeaderView;
[header setNeedsLayout];
[header layoutIfNeeded];
CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect frame = header.frame;
frame.size.height = height;
header.frame = frame;
self.tableView.tableHeaderView = header;
}
Solution 2
I encountered a similar problem when I was trying to set my table view header to a custom view I defined with auto layout using interface builder.
When I did so, I would find that it would be obscured by a section header.
My work around involved using a "dummy view" as the table header view and then adding my custom view to that dummy view as a subview. I think this allows auto layout to configure the appearance as per the constraints and the containing view's frame. This is probably not as elegant as vokilam's solution, but it works for me.
CGRect headerFrame = CGRectMake(0, 0, yourWidth, yourHeight);
UIView *tempView = [[UIView alloc] initWithFrame:headerFrame];
[tempView setBackgroundColor:[UIColor clearColor]];
YourCustomView *customView = [[YourCustomView alloc] initWithFrame: headerFrame];
[tempView addSubview:movieHeader];
self.tableView.tableHeaderView = tempView;
Solution 3
Because your tableHeaderView
is inited form xib
with auto layout, so the constraints of your custom headerView
and the superView
is unknown.You should add the constraints
of your custom headerView
:
1.in viewDidLLoad
assign your custom headerView to the tableView's tableHeaderView
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *yourHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"yourHeaderView" owner:nil options:nil] objectAtIndex:0];
//important:turn off the translatesAutoresizingMaskIntoConstraints;apple documents for details
yourHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
self.tableView.tableHeaderView = yourHeaderView;
}
2.in - (void)updateViewConstraints,add the essential constraints of your custom headerView
- (void)updateViewConstraints
{
NSDictionary *viewsDictionary = @{@"headerView":yourHeaderView};
NSArray *constraint_V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[headerView(121)]"
options:0
metrics:nil
views:viewsDictionary];
NSArray *constraint_H = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[headerView(320)]"
options:0
metrics:nil
views:viewsDictionary];
[self.headerView addConstraints:constraint_H];
[self.headerView addConstraints:constraint_V];
NSArray *constraint_POS_V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary];
NSArray *constraint_POS_H = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary];
[self.tableView addConstraints:constraint_POS_V];
[self.tableView addConstraints:constraint_POS_H];
[super updateViewConstraints];
}
OK! PS:Here is the related document:Resizing the View Controller’s Views
Solution 4
What solved my issue was this:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
private func sizeHeaderToFit() {
if let headerView = tableView.tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var newFrame = headerView.frame
// Needed or we will stay in viewDidLayoutSubviews() forever
if height != newFrame.size.height {
newFrame.size.height = height
headerView.frame = newFrame
tableView.tableHeaderView = headerView
}
}
}
I found this solution somewhere and worked like charm, I can't remember where though.
Solution 5
I found vokilam's answer to work great. Here's his solution in Swift.
func sizeHeaderToFit() {
guard let header = tableView.tableHeaderView else { return }
header.setNeedsLayout()
header.layoutIfNeeded()
let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
header.frame.size.height = height
tableView.tableHeaderView = header
}
jedipixel
Updated on March 03, 2020Comments
-
jedipixel about 4 years
I am trying to create a header view for my uiTableView (not a section header, I already have those.) I have set up an XIB in interface builder. All the connections are hooked up and it runs beautifully... except the table doesn't give it enough room! My problem is that the top of the table overlaps the table's header by a little.
My XIB is setup with autlayout for all the buttons, and IB is happy that the constraints don't conflict / ambiguous. The view is set to Freeform size, which in my case ended up being 320 x 471. Then in constraints for the view, I set an intrinsic size for the view of the same.
Now this works perfectly with my table. Everything looks great. But if I manually change any of the fonts in the header view with code, the layout makes the view bigger, and it ends up underneath my table.
Any ideas how to get the tableviewcontroller to leave enough room for the header view after setting fonts and sizes? I hope I've made sense explaining this.
-
idStar about 10 yearsWhile it would be nice for AutoLayout to just extend the height of the table header or footer based on constraints, using the above approach you've provided @vokilam, is the cleanest I've seen (and it worked for me).
-
gabriel14 almost 10 yearsWhat helped me was reading and reassigning:
UIView *header = self.tableView.tableHeaderView;
andself.tableView.tableHeaderView = header;
. I'm curious why. -
kjam over 9 yearsIs there a way to trigger execution of this code by observing autolayout rather then calling this function directly from all places that may change autolayout?
-
Josh Brown about 9 yearsWorks absolutely perfectly for me - the only thing I didn't see in the answer is where to call this. I put a call in
viewDidLayoutSubviews
and it worked for me. -
Josh Brown about 9 yearsActually, it turns out that
viewDidAppear
is a safer place to do this, especially if you're subclassingUITableViewController
. I wrote a full, more in-depth tutorial on table header view sizing in Swift that should help. -
Martin almost 9 yearsI like your approach, but it does not work for me... The height found is correct. But even but manually setting the header height Mr. Autolayout don't even care and enlarge my view as he wants. I hate you Mr. Autolayout.
-
Albert Bori almost 9 yearsThis worked for me. It is also completely flexible across orientations, if you use an equal-widths constraint on the tableView.
-
ClockWise over 8 yearsThis does work for me as well...on an iPhone 6 Plus with iOS 9.1, but doesn't work on my iPhone 5s iOS 8.0, or iPhone 4s iOS 8.1. Placing this call in viewDidLayoutSubviews does really weird things, it keeps calling viewDidLayoutSubview recursively...and placing the code somewhere else doesn't work either, I've placed the code somewhere I know is called after viewDidLayoutSubviews. Any ideas?
-
Chris C about 8 yearsJust as a heads up, in Swift you no longer need to create a placeholder variable when changing frame values.
header.frame.height = height
will work fine. -
ArpitM about 8 yearsOh cool, thanks! I updated it with just one tweak
header.frame.size.height = height
. Thanks for the heads up. -
Bruno Galinari about 8 yearsNeat solution! Here I got a 1px-line glitch between tableHeaderView and the first section (Not even a logic 1px, an actual 1px). It turns out the height calculation was 277.5, and adding
frame.size.height = round(height)
did the trick. -
Vasily over 7 years@ClockWise I found such behavior when you're using
UITableViewController
, because it calls layout again every time when you set tableView header. I recommend to switch toUIViewController
and embeddedUITableView
inside it, that will eliminate the problem. -
atulkhatri about 7 yearsI think this is the most elegant solution. Thanks a lot.
-
echappy about 7 yearsalthough not the cleanest, this worked for me, was quick, and allowed me to move on with my life
-
Chris almost 7 yearsI had to change the
let height
to a fixed value but this solved my problem.