首页 文章

AutoLayout多行UILabel切断了一些文字

提问于
浏览
29

我正在尝试学习自动布局,最后我想在发生这种情况时我正在 grab 它 . 我正在玩原型细胞和标签 . 我想要一个

  • Headers , titleLbl - 图片中的蓝框

  • Money, moneyLbl - 图片中的绿框

  • 字幕/描述, subTitleLbl - 图片中的红框

到目前为止我有这个:

iphone screenshot

我想要实现的是:

  • 绿框应始终完全显示在1行

  • 绿框内的值应基于蓝框居中

  • 蓝色框将占用剩余空间(-8px表示2之间的水平约束)并在必要时显示多行

  • 红色框应位于下方,并根据需要显示尽可能多的行 .

正如您所看到的,除了最后一行中的示例之外,这几乎都可以正常工作 . 我试图对此进行研究,从我可以收集的内容来看,这与内在的内容大小有关 . 很多在线帖子都建议设置 setPreferredMaxLayoutWidth ,我已经在 titleLbl 的多个地方完成了这个并且它没有任何效果 . 只有在将其硬编码到 180 时才能得到结果,但它还在文本顶部/下方的其他蓝框中添加了空格/填充,大大增加了红/蓝框之间的空间 .

这是我的约束:

Headers :
title constraints

钱:
money constraints

副 Headers :
subtitle constraints

基于我与其他文本样本一起运行的测试,它看起来确实使用了故事板中显示的宽度,但任何纠正它的尝试都不起作用 .

我创建了一个单元格的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:willDisplayCelltableView:heightForRowAtIndexPath: 中根据其他问题的答案设置此项 . 但不管它只是没有布局 .

任何帮助,想法或意见将不胜感激

4 回答

  • 1

    阅读本文后找到了更好的答案: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 始终正确 .

    Update:

    经过几次释放后,我发现上述情况并不适合每一个案例 . 从iOS 8开始(我相信),在某些情况下,只有在屏幕加载之前及时调用 didSet bounds .

    在最近的iOS 9中,当使用带有 UITableView 的模态 UIVIewController 时,我遇到了另一个问题,即 layoutSubviewsset bounds 被称为 after 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
        }
    }
    
  • 27

    我不确定我理解你是对的,我的解决方案与最好的解决方案相差甚远,但这里是:

    我在已切割的标签上添加了一个高度约束,将该约束连接到单元的出口 . 我已经覆盖了layoutSubview方法:

    - (void) layoutSubviews {
        [super layoutSubviews];
    
        [self.badLabel setNeedsLayout];
        [self.badLabel layoutIfNeeded];
    
        self.badLabelHeightConstraint.constant = self.badLabel.intrinsicContentSize.height;
    }
    

    和中提琴!请尝试该方法并告诉我结果,谢谢 .

  • 2

    我确实花了一些时间来整理细胞,看起来像这样:

    +--------------------------------------------------+
    | 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;
        }
    }
    
  • 0

    哦,只是有同样的问题 . @Simon McLoughlin的回答对我没有帮助 . 但

    titleLabel.adjustsFontSizeToFitWidth = true
    

    做了一个魔术!它有效,我不知道为什么,但确实如此 . 和是的,您的标签必须具有明确的preferredMaxLayoutWidth值 .

相关问题