Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I set the height of tableHeaderView (UITableView) with autolayout?

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.

enter image description here

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; } 
like image 371
Andrew Cross Avatar asked Jan 07 '14 21:01

Andrew Cross


Video Answer


2 Answers

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?

like image 130
TomSwift Avatar answered Oct 08 '22 21:10

TomSwift


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

AutoResizingHeader

like image 20
p-sun Avatar answered Oct 08 '22 20:10

p-sun