ios 如何使用自动布局设置 tableHeaderView (UITableView) 的高度?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20982558/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I set the height of tableHeaderView (UITableView) with autolayout?
提问by Andrew Cross
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.
在过去的 3 或 4 个小时里,我一直用这个头撞墙,我似乎无法弄清楚。我有一个 UIViewController,里面有一个全屏 UITableView(屏幕上还有其他一些东西,这就是我不能使用 UITableViewController 的原因),我想让我的 tableHeaderView 使用自动布局调整大小。不用说,这不是合作。
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.
因为overviewLabel(例如“在这里列出概览信息”文本)具有动态内容,我使用自动布局来调整它的大小并且它是超级视图。我已经很好地调整了所有东西的大小,除了 tableHeaderView,它就在层次结构中的视差表视图的正下方。
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):
在这种情况下,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);
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.
手动计算有效,但如果将来发生变化,它很笨重并且容易出错。我想要做的是让 tableHeaderView 根据提供的约束自动调整大小,就像您可以在其他任何地方一样。但对于我的生活,我无法弄清楚。
There's several posts on SO about this, but none are clear and ended up confusing me more. Here's a few:
有几篇关于这个的帖子,但没有一个是清楚的,最终让我更加困惑。这里有一些:
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.
将 translatesAutoresizingMaskIntoConstraints 属性更改为 NO 并没有真正意义,因为这只会对我造成错误并且在概念上没有任何意义。
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.
编辑 1:多亏了 TomSwift 的建议,我才能弄清楚。我可以按如下方式为我计算,而不是手动计算概览的高度,然后像以前一样重新设置 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];
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:
编辑 2:正如其他人所指出的,在编辑 1 中发布的解决方案似乎在 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;
}
采纳答案by TomSwift
You need to use the UIView systemLayoutSizeFittingSize:
method to obtain the minimum bounding size of your header view.
您需要使用该UIView systemLayoutSizeFittingSize:
方法来获取标题视图的最小边界大小。
I provide further discussion on using this API in this Q/A:
我在此 Q/A 中提供了有关使用此 API 的进一步讨论:
How to resize superview to fit all subviews with autolayout?
回答by Daniel Galasko
I really battled with this one and plonking the setup into viewDidLoad didn't work for me since the frame is not set in viewDidLoad, I also ended up with tons of messy warnings where the encapsulated auto layout height of the header was being reduced to 0. I only noticed the issue on iPad when presenting a tableView in a Form presentation.
我真的与这个斗争并且将设置放入 viewDidLoad 对我不起作用,因为框架没有在 viewDidLoad 中设置,我还得到了大量混乱的警告,其中标题的封装自动布局高度被减少到 0 . 我只在 iPad 上在 Form 演示文稿中展示 tableView 时注意到这个问题。
What solved the issue for me was setting the tableViewHeader in viewWillLayoutSubviews rather than in viewDidLoad.
对我来说解决这个问题的是在 viewWillLayoutSubviews 而不是 viewDidLoad 中设置 tableViewHeader。
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
}
}
回答by p-sun
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 项目以查看此操作。 https://github.com/p-sun/Swift2-iOS9-UI
回答by Kim André Sand
Your solution using systemLayoutSizeFittingSize: works if the header view is just updated once on each view appearance. In my case, the header view updated multiple times to reflect status changes. But systemLayoutSizeFittingSize: always reported the same size. That is, the size corresponding to the first update.
如果标题视图在每个视图外观上只更新一次,则使用 systemLayoutSizeFittingSize: 的解决方案有效。就我而言,标题视图更新了多次以反映状态变化。但是 systemLayoutSizeFittingSize: 总是报相同的大小。即第一次更新对应的大小。
To get systemLayoutSizeFittingSize: to return the correct size after each update I had to first remove the table header view before updating it and re-adding it:
要获得 systemLayoutSizeFittingSize: 在每次更新后返回正确的大小,我必须先删除表标题视图,然后再更新它并重新添加它:
self.listTableView.tableHeaderView = nil;
[self.headerView removeFromSuperview];
回答by petrsyn
This solution resizes the tableHeaderView and avoids infinite loop in the viewDidLayoutSubviews()
method I was having with some of the other answers here:
这个解决方案调整了 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
}
}
}
See also this post: https://stackoverflow.com/a/34689293/1245231
回答by Ravi Randeria
This worked for me on ios10 and Xcode 8
这在 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
}
回答by Sai kumar Reddy
It works for both header view and footer just replace the header with footer
它适用于页眉视图和页脚只需用页脚替换页眉
func sizeHeaderToFit() {
if let headerView = tableView.tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
}
回答by RunLoop
For iOS 12 and above, the following steps will ensure autolayout works properly in both your table and the header.
对于 iOS 12 及更高版本,以下步骤将确保自动布局在您的表格和标题中正常工作。
- Create your tableView first, then the header.
- At the end of your header creation code, call:
- 首先创建您的 tableView,然后是标题。
- 在标头创建代码的末尾,调用:
[headerV setNeedsLayout]; [headerV layoutIfNeeded];
[headerV setNeedsLayout]; [headerV layoutIfNeeded];
- Upon orientation change, mark the header for layout again and reload the table, this needs to happen slightly after the orientation change is reported:
- 方向更改后,再次标记布局的标题并重新加载表格,这需要在报告方向更改后稍微发生:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 *NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [tableV.tableHeaderView setNeedsLayout]; [tableV.tableHeaderView layoutIfNeeded]; [tableV reloadData];});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 *NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [tableV.tableHeaderView setNeedsLayout]; [tableV.tableHeaderView layoutIfNeeded]; [tableV reloadData];});
回答by meow2x
This solution works perfectly for me:
这个解决方案非常适合我:
https://spin.atomicobject.com/2017/08/11/swift-extending-uitableviewcontroller/
https://spin.atomicobject.com/2017/08/11/swift-extending-uitableviewcontroller/
It extends the UITableViewController. But if you are just using a UITableView, it will still work, just extend the UITableView instead of the UITableViewController.
Call the methods sizeHeaderToFit()
or sizeFooterToFit()
whenever there is an event that changes the tableViewHeader
height.
它扩展了 UITableViewController。但是如果你只是使用 UITableView,它仍然可以工作,只需扩展 UITableView 而不是 UITableViewController。调用方法sizeHeaderToFit()
或sizeFooterToFit()
每当有改变tableViewHeader
高度的事件时。
回答by Denis Kutlubaev
In my case viewDidLayoutSubviews
worked better. viewWillLayoutSubviews
causes white lines of a tableView
to appear. Also I added checking if my headerView
object already exists.
就我而言,viewDidLayoutSubviews
效果更好。viewWillLayoutSubviews
导致 a 的白线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];
}
}