如何设置tableHeaderView(UITableView)与自动布局的高度?

过去的3或4个小时,我一直在用这个砸碎我的头靠在墙上,我似乎无法弄清楚。 我有一个UIViewController里面有一个全屏UITableView(还有一些其他的东西在屏幕上,这就是为什么我不能使用UITableViewController),我想让我的tableHeaderView调整与自动布局。 不用说,这不合作。

看下面的截图。

在这里输入图像说明

因为overviewLabel(例如“List overview information here”文本)具有dynamic内容,所以我使用autolayout来调整它的大小,它是超级视图。 我已经把所有的东西都调整好了,除了tableHeaderView,它恰好位于hiearchy的Paralax Table View之下。

我发现调整这个头视图的唯一方法是编程方式,使用下面的代码:

CGRect headerFrame = self.headerView.frame; headerFrame.size.height = headerFrameHeight; self.headerView.frame = headerFrame; [self.listTableView setTableHeaderView:self.headerView]; 

在这种情况下,headerFrameHeight是手动计算tableViewHeader的高度如下(innerHeaderView是白色区域,或者第二个“View”,headerView是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); 

手动计算是可行的,但是如果事情在将来发生变化的话,它会很笨重,容易出错。 我希望能够做的是有tableHeaderView自动resize的基础上提供的限制,就像你可以在任何地方。 但是对于我的生活,我无法弄清楚。

关于这个,有几个post,但没有一个是清楚的,最终使我更困惑。 这里有几个:

  • 在UITableViewHeader上自动布局

  • tableHeaderView的自动布局

  • 是否有可能与UITableView的tableHeaderView使用AutoLayout?

  • 使用自动布局,IB和字体大小时,表格标题视图高度错误

将translatesAutoresizingMaskIntoConstraints属性更改为NO没有任何意义,因为这只会对我造成错误,无论如何也无济于事。

任何帮助将真正感激!

编辑1:谢谢TomSwift的build议,我能弄明白。 而不是手动计算总览的高度,我可以按如下计算,然后像以前一样重新设置tableHeaderView。

 [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]; 

编辑2:正如其他人已经注意到,编辑1中发布的解决scheme似乎不工作在viewDidLoad。 但是,它似乎在viewWillLayoutSubviews中工作。 示例代码如下:

 // 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; } 

您需要使用UIView systemLayoutSizeFittingSize:方法来获取您的标题视图的最小边界大小。

我在这个Q / A中提供了使用这个API的进一步讨论:

如何调整superview以适应所有子视图与自动布局?

我真的与这一个战斗,并将设置放入viewDidLoad没有为我工作,因为帧没有在viewDidLoad中设置,我也结束了吨的杂乱警告封装自动布局高度的标题被减less到0 。我只在桌面上呈现tableView时注意到了iPad上的问题。

什么解决了我的问题是设置tableViewHeader viewWillLayoutSubviews而不是在viewDidLoad。

 func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() if tableView.tableViewHeaderView == nil { let header: MyHeaderView = MyHeaderView.createHeaderView() header.setNeedsUpdateConstraints() header.updateConstraintsIfNeeded() header.frame = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGFloat.max) var newFrame = header.frame header.setNeedsLayout() header.layoutIfNeeded() let newSize = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) newFrame.size.height = newSize.height header.frame = newFrame self.tableView.tableHeaderView = header } } 

我发现了一种优雅的方式来使用自动布局调整表头,有和没有animation。

只需将其添加到您的视图控制器。

 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() } } 

要根据dynamic更改的标签resize:

 @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) } 

根据约束条件的变化来dynamicresize:

 @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() } } 

看到AutoResizingHeader项目看到这个在行动。 https://github.com/p-sun/Swift2-iOS9-UI

AutoResizingHeader

使用systemLayoutSizeFittingSize的解决scheme:如果标题视图在每个视图外观上只更新一次,则工作。 在我的情况下,标题视图多次更新,以反映状态的变化。 但是systemLayoutSizeFittingSize:总是报告相同的大小。 也就是说,大小对应于第一次更新。

要获得systemLayoutSizeFittingSize:要在每次更新后返回正确的大小,我必须首先删除表头视图,然后重新添加它:

 self.listTableView.tableHeaderView = nil; [self.headerView removeFromSuperview]; 

这在ios10和Xcode 8上为我工作

 func layoutTableHeaderView() { guard let headerView = tableView.tableHeaderView else { return } headerView.translatesAutoresizingMaskIntoConstraints = false let headerWidth = headerView.bounds.size.width; let temporaryWidthConstraints = NSLayoutConstraint.constraintsWithVisualFormat("[headerView(width)]", options: NSLayoutFormatOptions(rawValue: UInt(0)), metrics: ["width": headerWidth], views: ["headerView": headerView]) headerView.addConstraints(temporaryWidthConstraints) headerView.setNeedsLayout() headerView.layoutIfNeeded() let headerSize = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) let height = headerSize.height var frame = headerView.frame frame.size.height = height headerView.frame = frame self.tableView.tableHeaderView = headerView headerView.removeConstraints(temporaryWidthConstraints) headerView.translatesAutoresizingMaskIntoConstraints = true } 

在我的情况viewDidLayoutSubviews更好地工作。 viewWillLayoutSubviews会导致出现一个tableView白线。 另外我添加检查,如果我的headerView对象已经存在。

 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; if ( ! self.userHeaderView ) { // Setup HeaderView self.userHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"SSUserHeaderView" owner:self options:nil] objectAtIndex:0]; [self.userHeaderView setNeedsLayout]; [self.userHeaderView layoutIfNeeded]; CGFloat height = [self.userHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; CGRect headerFrame = self.userHeaderView.frame; headerFrame.size.height = height; self.userHeaderView.frame = headerFrame; self.tableView.tableHeaderView = self.userHeaderView; // Update HeaderView with data [self.userHeaderView updateWithProfileData]; } } 

这个解决scheme调整了tableHeaderView的大小,并避免了viewDidLayoutSubviews()方法中的无限循环,这里有一些其他的答案:

 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let headerView = tableView.tableHeaderView { let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height var headerFrame = headerView.frame // comparison necessary to avoid infinite loop if height != headerFrame.size.height { headerFrame.size.height = height headerView.frame = headerFrame tableView.tableHeaderView = headerView } } } 

另见这个post: https : //stackoverflow.com/a/34689293/1245231