UITableView 标头上的自动布局
Posted
技术标签:
【中文标题】UITableView 标头上的自动布局【英文标题】:Auto Layout on UITableView header 【发布时间】:2013-09-17 19:57:05 【问题描述】:我一直在寻找关于如何将自动布局添加到 UITableView 的明确答案。到目前为止,我的代码如下所示:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
UINib *nib = [UINib nibWithNibName:@"HomeHeaderView" bundle:nil];
UIView *headerView = (UIView *)[nib instantiateWithOwner:self options:nil][0];
[headerView.layer setCornerRadius:6.0];
[headerView setTranslatesAutoresizingMaskIntoConstraints:NO];
// NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(headerView);
// NSMutableArray *headerConstraints = [[NSMutableArray alloc] init];
// [headerConstraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[headerView]-|" options:0 metrics:nil views:viewsDictionary]];
// [headerConstraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[headerView]-|" options:0 metrics:nil views:viewsDictionary]];
// [self.actionsTableView addConstraints:headerConstraints];
// [self.view addSubview:headerView];
tableView.tableHeaderView = headerView;
[headerView layoutSubviews];
NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:headerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:headerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:headerView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1 constant:300];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:headerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1 constant:90];
[self.view addConstraints:@[centerX, centerY, width, height]];
return headerView;
我的标题视图基本上有一个 nib 文件,我想将该 nib 放在 UITableViewHeader 的中心。我希望它在纵向和横向上相应地增长和缩小。老实说,我不确定我是否正确设置了约束。我不确定我的toItem
应该是视图控制器的视图,还是表视图本身。
我也不知道是否应该将 headerview 作为子视图添加到视图控制器的视图或 tableview 本身。
或者,我不确定设置 tableView.tableHeaderView = headerView 是否足够。
我真的不知道这样的最佳实践是什么。我不确定这一切是否也可以在 IB 中完成。目前,使用您看到的代码,我收到此错误:
'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'
正是因为那个错误,我才加了[headerView layoutSubviews]
对此有什么想法?提前致谢!
【问题讨论】:
【参考方案1】:真正的问题是您将viewForHeaderInSection:
与表格的headerView
混淆了。它们是无关的。
前者是 section 的标题。您从委托方法返回视图。
后者是表格的标题。您设置视图,可能在您的viewDidLoad
。
约束以正常方式运行。但它们应该只是对其子视图的内部约束。在您形成它时,该视图不在您的界面中。而且它的大小和位置在当时并不取决于你。如果是节标题,它将自动调整大小以正确适应(根据表格的宽度和表格或委托对标题高度的声明)。如果是表头,你可以给它一个绝对高度,但它的宽度会被调整到合适的大小。
这里是一个完整的例子,它构建了一个在其子视图上具有内部约束的节头。
- (UIView *)tableView:(UITableView *)tableView
viewForHeaderInSection:(NSInteger)section
UITableViewHeaderFooterView* h =
[tableView dequeueReusableHeaderFooterViewWithIdentifier:@"Header"];
if (![h.tintColor isEqual: [UIColor redColor]])
h.tintColor = [UIColor redColor];
h.backgroundView = [UIView new];
h.backgroundView.backgroundColor = [UIColor blackColor];
UILabel* lab = [UILabel new];
lab.tag = 1;
lab.font = [UIFont fontWithName:@"Georgia-Bold" size:22];
lab.textColor = [UIColor greenColor];
lab.backgroundColor = [UIColor clearColor];
[h.contentView addSubview:lab];
UIImageView* v = [UIImageView new];
v.tag = 2;
v.backgroundColor = [UIColor blackColor];
v.image = [UIImage imageNamed:@"us_flag_small.gif"];
[h.contentView addSubview:v];
lab.translatesAutoresizingMaskIntoConstraints = NO;
v.translatesAutoresizingMaskIntoConstraints = NO;
[h.contentView addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:@"H:|-5-[lab(25)]-10-[v(40)]"
options:0 metrics:nil views:@@"v":v, @"lab":lab]];
[h.contentView addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|[v]|"
options:0 metrics:nil views:@@"v":v]];
[h.contentView addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|[lab]|"
options:0 metrics:nil views:@@"lab":lab]];
UILabel* lab = (UILabel*)[h.contentView viewWithTag:1];
lab.text = self.sectionNames[section];
return h;
【讨论】:
您能否提供代码 sn-ps 以说明如何进行?在这一点上,我仍然对如何对节标题或 tableview 标题进行约束感到困惑:-\。 您以正常方式进行操作。但它们应该只是对其子视图的内部约束。在您形成它时,该视图不在您的界面中。如果它是节标题,它将自动调整大小以正确适应。如果是表头,你可以给它一个绝对高度,但它的宽度会被调整到合适的大小。 不幸的是,它不起作用。UITableViewHeaderFooterView
的 contentView
已计算大小,您将在运行时收到 Unable to simultaneously satisfy constraints
警告。看起来虽然可以使用 AutoLayout 自动计算单元格高度,但对于节标题却不能这样做。刚刚在 ios 8.1 上检查过。
@matt 如果您以一种应该拉伸标题以适应其内容的方式设置约束 - 它在 iOS 8 上不起作用。您将得到 Unable to simultaneously satisfy constraints
就像我说的那样。如果您将自定义子视图直接添加到标题视图而不是 contentView
- 它会起作用。
@matt 这与喜欢与否无关 :-) 您很好地解释了如何将带有约束的自定义视图添加到标题视图。我同意这是这样做的正确方法。不幸的是,它会破坏 iOS 8 自动管理的约束。我只是展示了如何解决该问题以实现自动调整标题大小。您可以返回UITableViewAutomaticDimension
,而不是在heightForHeaderInSection
中定义固定高度,它会自动调整标题高度。【参考方案2】:
我发现provided by matt 的解决方案可能并不完美,因为他正在向UITableViewHeaderFooterView
的contentView
添加自定义视图和约束。这总是在运行时导致自动布局警告:Unable to simultaneously satisfy constraints
当我们想要动态标题高度时。
我不确定原因,但我们可以假设 iOS 为 contentView
添加了一些额外的约束,用于设置该视图的固定宽度和高度。运行时生成的警告告诉我们手动添加的约束不能满足这些约束,这很明显,因为我们的约束应该拉伸标题视图,以便子视图可以适应它。
解决方案非常简单——不要使用UITableViewHeaderFooterView
的contentView
,只需将您的子视图直接添加到UITableViewHeaderFooterView
。我可以确认它在 iOS 8.1 上没有任何问题。如果您想添加多个视图并更改标题的背景颜色,请考虑添加填充标题视图的UIView
(感谢 AutoLayout 约束),然后添加您希望对该视图具有的所有子视图(我称之为@ 987654330@)。这样我们就可以避免任何 AutoLayout 问题,并在 UITableView
中自动调整标题。
【讨论】:
【参考方案3】:这是一个巧妙的解决方案:
可选:initWithStyle:UITableViewStyleGrouped 防止浮动tableViewHeader
制作两个属性,标签只是为了演示:
@property (nonatomic, strong, readwrite) UIView *headerView;
@property (nonatomic, strong, readwrite) UILabel *headerLabel;
在 viewDidLoad 中设置所有内容:
self.headerView = [[UIView alloc] initWithFrame:CGRectZero];
self.headerLabel = [[UILabel alloc] init];
self.headerLabel.text = @"Test";
self.headerLabel.numberOfLines = 0; //unlimited
self.headerLabel.textAlignment = NSTextAlignmentCenter;
self.headerLabel.translatesAutoresizingMaskIntoConstraints = NO; //always set this to NO when using AutoLayout
[self.headerView addSubview:self.headerLabel];
NSString *horizontalFormat = @"H:|-[headerLabel]-|";
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:horizontalFormat options:0 metrics:nil views:@@"headerLabel":self.headerLabel];
[self.headerView addConstraints:horizontalConstraints];
NSString *verticalFormat = @"V:|-[headerLabel]-|";
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:verticalFormat options:0 metrics:nil views:@@"headerLabel":self.headerLabel];
[self.headerView addConstraints:verticalConstraints];
在viewForHeaderInSection中:
return self.headerView;
在 heightForHeaderInSection:
self.headerLabel.preferredMaxLayoutWidth = tableView.bounds.size.width;
return [self.headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
【讨论】:
这种方法很棒。谢谢。以上是关于UITableView 标头上的自动布局的主要内容,如果未能解决你的问题,请参考以下文章
在 UITableView 标头中将标签约束设置为 0 时自动布局失败