I'm been smashing my head against the wall with this for last 3 or 4 hours and I can't seem to figure it out. I have a UIViewController with a full screen UITableView inside of it (there's some other stuff on the screen, which is why I can't use a UITableViewController) and I want to get my tableHeaderView to resize with autolayout. Needless to say, it's not cooperating.
See screenshot below.
Because the overviewLabel (e.g. the "List overview information here." text) has dynamic content, I'm using autolayout to resize it and it's superview. I've got everything resizing nicely, except for the tableHeaderView, which is right below Paralax Table View in the hiearchy.
The only way I've found to resize that header view is programatically, with the following code:
CGRect headerFrame = self.headerView.frame; headerFrame.size.height = headerFrameHeight; self.headerView.frame = headerFrame; [self.listTableView setTableHeaderView:self.headerView];
In this case, headerFrameHeight is a manual calculation of the tableViewHeader height as follows (innerHeaderView is the white area, or the second "View", headerView is tableHeaderView):
CGFloat startingY = self.innerHeaderView.frame.origin.y + self.overviewLabel.frame.origin.y; CGRect overviewSize = [self.overviewLabel.text boundingRectWithSize:CGSizeMake(290.f, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: self.overviewLabel.font} context:nil]; CGFloat overviewHeight = overviewSize.size.height; CGFloat overviewPadding = ([self.overviewLabel.text length] > 0) ? 10 : 0; // If there's no overviewText, eliminate the padding in the overall height. CGFloat headerFrameHeight = ceilf(startingY + overviewHeight + overviewPadding + 21.f + 10.f);
The manual calculation works, but it's clunky and prone to error if things change in the future. What I want to be able to do is have the tableHeaderView auto-resize based on the provided constraints, like you can anywhere else. But for the life of me, I can't figure it out.
There's several posts on SO about this, but none are clear and ended up confusing me more. Here's a few:
Auto layout on UITableViewHeader
Auto Layout for tableHeaderView
Is it possible to use AutoLayout with UITableView's tableHeaderView?
table header view height is wrong when using auto layout, IB, and font sizes
It doesn't really make sense to change the translatesAutoresizingMaskIntoConstraints property to NO, since that just causes errors for me and doesn't make sense conceptually anyway.
Any help would really be appreciated!
EDIT 1: Thanks to TomSwift's suggestion, I was able to figure it out. Instead of manually calculating the height of the overview, I can have it calculated for me as follows and then re-set the tableHeaderView as before.
[self.headerView setNeedsLayout]; [self.headerView layoutIfNeeded]; CGFloat height = [self.innerHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + self.innerHeaderView.frame.origin.y; // adding the origin because innerHeaderView starts partway down headerView. CGRect headerFrame = self.headerView.frame; headerFrame.size.height = height; self.headerView.frame = headerFrame; [self.listTableView setTableHeaderView:self.headerView];
Edit 2: As others have noted, the solution posted in Edit 1 doesn't seem to work in viewDidLoad. It does, however, seem to work in viewWillLayoutSubviews. Example code below:
// Note 1: The variable names below don't match the variables above - this is intended to be a simplified "final" answer. // Note 2: _headerView was previously assigned to tableViewHeader (in loadView in my case since I now do everything programatically). // Note 3: autoLayout code can be setup programatically in updateViewConstraints. - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; [_headerWrapper setNeedsLayout]; [_headerWrapper layoutIfNeeded]; CGFloat height = [_headerWrapper systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; CGRect headerFrame = _headerWrapper.frame; headerFrame.size.height = height; _headerWrapper.frame = headerFrame; _tableView.tableHeaderView = _headerWrapper; }
You need to use the UIView systemLayoutSizeFittingSize:
method to obtain the minimum bounding size of your header view.
I provide further discussion on using this API in this Q/A:
How to resize superview to fit all subviews with autolayout?
I've found an elegant way to way to use auto layout to resize table headers, with and without animation.
Simply add this to your View Controller.
func sizeHeaderToFit(tableView: UITableView) { if let headerView = tableView.tableHeaderView { let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height var frame = headerView.frame frame.size.height = height headerView.frame = frame tableView.tableHeaderView = headerView headerView.setNeedsLayout() headerView.layoutIfNeeded() } }
To resize according to a dynamically changing label:
@IBAction func addMoreText(sender: AnyObject) { self.label.text = self.label.text! + "\nThis header can dynamically resize according to its contents." } override func viewDidLayoutSubviews() { // viewDidLayoutSubviews is called when labels change. super.viewDidLayoutSubviews() sizeHeaderToFit(tableView) }
To animate a resize according to a changes in a constraint:
@IBOutlet weak var makeThisTallerHeight: NSLayoutConstraint! @IBAction func makeThisTaller(sender: AnyObject) { UIView.animateWithDuration(0.3) { self.tableView.beginUpdates() self.makeThisTallerHeight.constant += 20 self.sizeHeaderToFit(self.tableView) self.tableView.endUpdates() } }
See the AutoResizingHeader project to see this in action. https://github.com/p-sun/Swift2-iOS9-UI
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With