在过去的3到4个小时里,我一直把头撞在墙上,我似乎无法弄明白。我有一个UIViewController,里面有一个全屏UITableView(屏幕上还有其他一些东西,这就是为什么我不能使用UITableViewController)我想让我的tableHeaderView用autolayout调整大小。不用说,它没有合作。
见下面的截图。
因为overviewLabel(例如"列表概述信息在这里。"文本)具有动态内容,所以我使用autolayout来调整它的大小和它的超级视图。除了tableHeaderView之外,我的所有内容都很好地调整了大小,它位于层次结构中的Paralax Table View之下。
我发现调整标题视图大小的唯一方法是以编程方式,使用以下代码:
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];
在这种情况下,headerFrameHeight是tableViewHeader高度的手动计算,如下所示(innerHeaderView是白色区域,或者第二个&#34; View&#34;,headerView是tableHeaderView):< / p>
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根据提供的约束自动调整大小,就像你可以在其他地方一样。但对于我的生活,我无法弄明白。
关于这一点,有几个关于此的帖子,但没有一个是清楚的,最终让我更加困惑。这里有一些:
Is it possible to use AutoLayout with UITableView's tableHeaderView?
table header view height is wrong when using auto layout, IB, and font sizes
将translatesAutoresizingMaskIntoConstraints属性更改为NO并不是真的有意义,因为这只会给我带来错误,并且无论如何都不会在概念上有意义。
真的很感激任何帮助!
编辑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];
编辑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;
}
答案 0 :(得分:35)
您需要使用UIView systemLayoutSizeFittingSize:
方法获取标题视图的最小边界大小。
我在本Q / A中提供了有关使用此API的进一步讨论:
How to resize superview to fit all subviews with autolayout?
答案 1 :(得分:21)
我真的和这个人争斗并且将设置放入viewDidLoad对我来说不起作用,因为在viewDidLoad中没有设置框架,我最后还发现了大量凌乱的警告,其中封装的自动布局高度是减少到0.我在表单演示文稿中呈现tableView时,才注意到iPad上的问题。
为我解决的问题是在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
}
}
答案 2 :(得分:17)
我找到了一种优雅的方式来使用自动布局来调整表格标题的大小,包括动画和不包含动画。
只需将其添加到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()
}
}
根据动态变化的标签调整大小:
@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)
}
根据约束中的更改为调整大小设置动画:
@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
答案 3 :(得分:3)
使用systemLayoutSizeFittingSize的解决方案:如果标题视图在每个视图外观上只更新一次,则有效。在我的例子中,标题视图多次更新以反映状态更改。但是systemLayoutSizeFittingSize:总是报告相同的大小。也就是说,对应于第一次更新的大小。
要获得systemLayoutSizeFittingSize:在每次更新后返回正确的大小,我必须先删除表头视图,然后再更新它并重新添加它:
self.listTableView.tableHeaderView = nil;
[self.headerView removeFromSuperview];
答案 4 :(得分:2)
这对我在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
}
答案 5 :(得分:2)
这个解决方案调整了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
}
}
}
答案 6 :(得分:1)
对于iOS 12及更高版本,以下步骤将确保自动布局在表格和标题中均正常工作。
[headerV setNeedsLayout]; [headerV layoutIfNeeded];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 *NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [tableV.tableHeaderView setNeedsLayout]; [tableV.tableHeaderView layoutIfNeeded]; [tableV reloadData];});
答案 7 :(得分:0)
就我而言,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];
}
}
答案 8 :(得分:0)
很有可能将基于AutoLayout的通用UIView
与任何AL内部子视图结构一起用作tableHeaderView
。
唯一需要的是在之前设置一个简单的tableFooterView
!
让self.headerView
是一些基于约束的UIView
。
- (void)viewDidLoad {
........................
self.tableView.tableFooterView = [UIView new];
[self.headerView layoutIfNeeded]; // to set initial size
self.tableView.tableHeaderView = self.headerView;
[self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES;
[self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
[self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES;
// and the key constraint
[self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
}
如果self.headerView
在UI旋转下更改高度,则必须实现
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition: ^(id<UIViewControllerTransitionCoordinatorContext> context) {
// needed to resize header height
self.tableView.tableHeaderView = self.headerView;
} completion: NULL];
}
可以为此目的使用 ObjC 类别
@interface UITableView (AMHeaderView)
- (void)am_insertHeaderView:(UIView *)headerView;
@end
@implementation UITableView (AMHeaderView)
- (void)am_insertHeaderView:(UIView *)headerView {
NSAssert(self.tableFooterView, @"Need to define tableFooterView first!");
[headerView layoutIfNeeded];
self.tableHeaderView = headerView;
[self.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor].active = YES;
[self.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES;
[self.topAnchor constraintEqualToAnchor:headerView.topAnchor].active = YES;
[self.tableFooterView.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES;
}
@end
还有 Swift 扩展
extension UITableView {
func am_insertHeaderView2(_ headerView: UIView) {
assert(tableFooterView != nil, "Need to define tableFooterView first!")
headerView.layoutIfNeeded()
tableHeaderView = headerView
leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
tableFooterView?.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
}
}
答案 9 :(得分:0)
它适用于页眉视图和页脚,只需将页眉替换为页脚
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
}
}
答案 10 :(得分:0)
此解决方案非常适合我:
https://spin.atomicobject.com/2017/08/11/swift-extending-uitableviewcontroller/
它扩展了UITableViewController。但是,如果您仅使用UITableView,它仍然可以工作,只需扩展UITableView而不是UITableViewController。
每当发生更改sizeHeaderToFit()
高度的事件时,调用方法sizeFooterToFit()
或tableViewHeader
。
答案 11 :(得分:0)
从this post复制
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).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
}
}
}