我正在尝试学习自动布局,最后我以为在发生这种情况时我会抓住它。我正在玩原型细胞和标签。我想要一个
titleLbl
- 图片中的蓝框moneyLbl
- 图片中的绿框subTitleLbl
- 图片中的红框到目前为止,我有这个:
我想要实现的目标是:
正如您所看到的,除了最后一行中的示例外,这几乎全部正常工作。我试图对此进行研究,从我可以收集的内容来看,它与内在的内容大小有关。很多在线帖子都建议设置setPreferredMaxLayoutWidth
,我已经在titleLbl
的多个位置完成了此操作,但它没有任何效果。只有在将其硬编码到180
时才能获得结果,但它还在文本顶部/下方的其他蓝框中添加了空格/填充,大大增加了红/蓝框之间的空间。
以下是我的约束:
名称:
金钱:
字幕:
根据我与其他文本样本一起运行的测试,它似乎使用了故事板中显示的宽度,但任何纠正它的尝试都不起作用。
我创建了一个单元格的ivar并用它来创建单元格的高度:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
[sizingCellTitleSubMoney.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]];
[sizingCellTitleSubMoney.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]];
[sizingCellTitleSubMoney.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]];
[sizingCellTitleSubMoney layoutIfNeeded];
CGSize size = [sizingCellTitleSubMoney.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height+1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MATitleSubtitleMoneyTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifierTitleSubTitleMoney];
if(!cell)
{
cell = [[MATitleSubtitleMoneyTableViewCell alloc] init];
}
cell.titleLbl.layer.borderColor = [UIColor blueColor].CGColor;
cell.titleLbl.layer.borderWidth = 1;
cell.subtitleLbl.layer.borderColor = [UIColor redColor].CGColor;
cell.subtitleLbl.layer.borderWidth = 1;
cell.moneyLbl.layer.borderColor = [UIColor greenColor].CGColor;
cell.moneyLbl.layer.borderWidth = 1;
[cell.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]];
[cell.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]];
[cell.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]];
return cell;
}
我已尝试在单元格的布局子视图中设置setPreferredMaxLayoutWidth
:
- (void)layoutSubviews
{
[super layoutSubviews];
NSLog(@"Money lbl: %@", NSStringFromCGRect(self.moneyLbl.frame));
NSLog(@"prefferredMaxSize: %f \n\n", self.moneyLbl.frame.origin.x-8);
[self.titleLbl setPreferredMaxLayoutWidth: self.moneyLbl.frame.origin.x-8];
}
日志:
2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] Money lbl: {{209, 20}, {91, 61}}
2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] prefferredMaxSize: 201.000000
这是我期望的,因为2个元素之间有8px,控制台会验证这一点。无论我在蓝色框中添加多少文本(与故事板相同),它在第一行上都能完美运行,但绿色框的任何增加都会导致宽度计算失败。我已尝试根据其他问题的答案在tableView:willDisplayCell
和tableView:heightForRowAtIndexPath:
中设置此项。但无论它不是什么布局都不会。
非常感谢任何帮助,想法或评论
答案 0 :(得分:27)
阅读本文后,找到了更好的答案:http://johnszumski.com/blog/auto-layout-for-table-view-cells-with-dynamic-heights
创建UILabel
子类以覆盖layoutSubviews
,如下所示:
- (void)layoutSubviews
{
[super layoutSubviews];
self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
[super layoutSubviews];
}
这可确保preferredMaxLayoutWidth
始终正确。
<强>更新强>
经过几次iOS发布并测试更多用例后,我发现上述内容并不适用于所有情况。从iOS 8开始(我相信),在某些情况下,只有didSet
bounds
在屏幕加载之前及时调用。
在最近的iOS 9中,当使用带有UIVIewController
的模态UITableView
时,我遇到了另一个问题,layoutSubviews
和set bounds
被称为之后 heightForRowAtIndexPath
。经过大量调试后,唯一的解决方案是覆盖set frame
。
以下代码现在似乎是必要的,以确保它适用于所有iOS,控制器,屏幕尺寸等。
override public func layoutSubviews()
{
super.layoutSubviews()
self.preferredMaxLayoutWidth = self.bounds.width
super.layoutSubviews()
}
override public var bounds: CGRect
{
didSet
{
self.preferredMaxLayoutWidth = self.bounds.width
}
}
override public var frame: CGRect
{
didSet
{
self.preferredMaxLayoutWidth = self.frame.width
}
}
答案 1 :(得分:2)
我不确定我是否理解你,我的解决方案与最好的解决方案相差甚远,但现在是:
我在已切割的标签上添加了一个高度限制,将该约束连接到单元格的插座。我已经覆盖了layoutSubview方法:
- (void) layoutSubviews {
[super layoutSubviews];
[self.badLabel setNeedsLayout];
[self.badLabel layoutIfNeeded];
self.badLabelHeightConstraint.constant = self.badLabel.intrinsicContentSize.height;
}
和中提琴!请尝试这种方法并告诉我结果,谢谢。
答案 2 :(得分:1)
我确实花了一些时间来整理细胞,看起来像这样:
+--------------------------------------------------+
| This is my cell.contentView |
| +------+ +-----------------+ +--------+ +------+ |
| | | | multiline | | title2 | | | |
| | img | +-----------------+ +--------+ | img | |
| | | +-----------------+ +--------+ | | |
| | | | title3 | | title4 | | | |
| +------+ +-----------------+ +--------+ +------+ |
| |
+--------------------------------------------------+
经过多次尝试,我确实找到了如何使用实际工作的原型单元的解决方案。
主要问题是我的标签可能存在或可能不存在。 每个元素都应该是可选的。
首先,我们的'原型'单元格应该只在我们不处于“真实”环境时布局。
因此,我确实创建了标志'heightCalculationInProgress'。
当某人(tableView的dataSource)要求计算高度时,我们使用块向原型单元格提供数据: 然后我们要求我们的原型单元布局内容并从中获取高度。简单。
为了避免iOS7与layoutSubview递归的错误,有一个“layoutingLock”变量
- (CGFloat)heightWithSetupBlock:(nonnull void (^)(__kindof UITableViewCell *_Nonnull cell))block {
block(self);
self.heightCalculationInProgress = YES;
[self setNeedsLayout];
[self layoutIfNeeded];
self.layoutingLock = NO;
self.heightCalculationInProgress = NO;
CGFloat calculatedHeight = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGFloat height = roundf(MAX(calculatedHeight, [[self class] defaultHeight]));
return height;
}
来自外部的“阻止”可能如下所示:
[prototypeCell heightWithSetupBlock:^(__kindof UITableViewCell * _Nonnull cell) {
@strongify(self);
cell.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 0);
cell.dataObject = dataObject;
cell.multilineLabel.text = @"Long text";
// Include any data here
}];
现在开始最有趣的部分。布点:
- (void)layoutSubviews {
if(self.layoutingLock){
return;
}
// We don't want to do this layouting things in 'real' cells
if (self.heightCalculationInProgress) {
// Because of:
// Support for constraint-based layout (auto layout)
// If nonzero, this is used when determining -intrinsicContentSize for multiline labels
// We're actually resetting internal constraints here. And then we could reuse this cell with new data to layout it correctly
_multilineLabel.preferredMaxLayoutWidth = 0.f;
_title2Label.preferredMaxLayoutWidth = 0.f;
_title3Label.preferredMaxLayoutWidth = 0.f;
_title4Label.preferredMaxLayoutWidth = 0.f;
}
[super layoutSubviews];
// We don't want to do this layouting things in 'real' cells
if (self.heightCalculationInProgress) {
// Make sure the contentView does a layout pass here so that its subviews have their frames set, which we
// need to use to set the preferredMaxLayoutWidth below.
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
// Set the preferredMaxLayoutWidth of the mutli-line bodyLabel based on the evaluated width of the label's frame,
// as this will allow the text to wrap correctly, and as a result allow the label to take on the correct height.
_multilineLabel.preferredMaxLayoutWidth = CGRectGetWidth(_multilineLabel.frame);
_title2Label.preferredMaxLayoutWidth = CGRectGetWidth(_title2Label.frame);
_title3Label.preferredMaxLayoutWidth = CGRectGetWidth(title3Label.frame);
_title4Label.preferredMaxLayoutWidth = CGRectGetWidth(_title4Label.frame);
}
if (self.heightCalculationInProgress) {
self.layoutingLock = YES;
}
}
答案 3 :(得分:0)
titleLabel.adjustsFontSizeToFitWidth = true
做了一个魔术!它有效,我不知道为什么,但确实如此。 是的,您的标签必须具有明确的preferredMaxLayoutWidth值。