如何以编程方式添加具有可变高度和布局ios的UILabel



我一直在寻找一整天,但无法使其工作。

我有一个带有两个标签的UIView,在另一个标签的上方,在左侧,在右侧有一个按钮。我添加了约束,以便autolayout相应地调整视图大小。当我为UIView的高度设置了一个约束(两个UILabel的高度没有一个约束)时,一切都运行良好,但是由于下UILabel的内容会有所不同,我删除了该约束并为UILabel设置了两个约束,一个固定用于上UILabel,另一个与关系"大于或等于"的关系用于下UILabel, 另一个用于固定下UILabelUIView之间的距离.

看起来自动布局无法计算下UILabelintrinsicContentSize,因为它永远不会将其高度增加到 10px 以上,尽管有较低UILabel的内容。

UIView *miView = [[UIView alloc] init];
UILabel *miLabel = [[UILabel alloc] init];
[miLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[miDetailsLabel setNumberOfLines:1];
[miDetailsLabel setText:@"Just one line."];
[miView addSubview:miLabel];
UILabel *miDetailsLabel = [[UILabel alloc] init];
[miDetailsLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[miDetailsLabel setNumberOfLines:0];
[miDetailsLabel setText:@"Enough text to show 3 lines on an IP4, except first of three UILabels on my test code, with no content"];
[miView addSubview:miDetailsLabel];
UIButton *miButton = [[UIButton alloc] init];
[miButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[miView addSubview:miButton];
NSArray *constraint_inner_h = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(0)-[label]-(10)-[button(52)]-(0)-|" options:0 metrics:nil views:@{@"label":miLabel, @"button":miButton}];
NSArray *constraint_inner2_h = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(0)-[details]-(10)-[button]-(0)-|" options:0 metrics:nil views:@{@"details":miDetailsLabel, @"button":miButton}];
NSArray *constraint_label_v = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(0)-[label(18)]-(2)-[details(>=10)]-(0)-|" options:0 metrics:nil views:@{@"label":miLabel, @"details":miDetailsLabel}];
NSArray *constraint_button_v = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[button(22)]" options:0 metrics:nil views:@{@"button":miButton}];
[miView addConstraint:[NSLayoutConstraint constraintWithItem:miButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:miView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[miView addConstraints:constraint_inner_h];
[miView addConstraints:constraint_inner2_h];
[miView addConstraints:constraint_label_v];
[miView addConstraints:constraint_button_v];

我放了一个简化版本的代码。

对我错过了什么有什么想法吗?

谢谢

更新:感谢Matt的建议,我尝试了此解决方案以将正确的值设置为preferredMawLayoutWidth:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    NSArray *allKeys = [dictOpcionesTickets allKeys];
    for (NSString *key in allKeys) {
        NSArray *tmpArray = [dictOpcionesTickets objectForKey:key];
        if (![[tmpArray objectAtIndex:0] isEqual:@""]) {
            UILabel *tmpLabel = [tmpArray objectAtIndex:3];
            tmpLabel.preferredMaxLayoutWidth = tmpLabel.frame.size.width;
            NSLog(@"width: %f",tmpLabel.frame.size.width);
        }
    }
    [self.view layoutIfNeeded];
}

为了解释代码,正如我所说,我是动态的,所以我创建了一个字典,其中包含一个数组,其中包含对我的UILabel的引用(以及其他有趣的信息)。当我运行代码时,我得到下一个日志(代码解析 3 UILabel,第一个没有内容的标签):

2014-12-28 20:40:42.898 myapp[5419:60b] width: 0.000000
2014-12-28 20:40:42.912 myapp[5419:60b] width: 0.000000
2014-12-28 20:40:43.078 myapp[5419:60b] width: 229.000000
2014-12-28 20:40:43.080 myapp[5419:60b] width: 229.000000
2014-12-28 20:40:43.326 myapp[5419:60b] width: 229.000000
2014-12-28 20:40:43.327 myapp[5419:60b] width: 229.000000

但我仍然得到相同的结果...UIView 仍然显示高度等于约束设置的最小高度,而不显示UILabel的内容。

更新 2 还在胡闹。

我已经测试了我的初始代码,但在 xcode 6 中的一个新项目上进行了简化,在带有 iOS7 的实际 iPhone 4 上运行,它无需设置preferredMaxLayoutWidth或子类化UILabel即可完美运行,甚至无需在父视图上调用layoutIfNeeded。但是当涉及到真正的项目时(我认为它最初是在 xcode 4 或 5 中构建的),它不起作用:

- (void)addLabelsDinamically {
    self.labelFixed.text = @"Below this IB label goes others added dinamically...";
    UIButton *miBoton = [[UIButton alloc] init];
    miBoton.translatesAutoresizingMaskIntoConstraints = NO;
    [miBoton setTitle:@"Hola" forState:UIControlStateNormal];
    [self.viewLabels addSubview:miBoton];
    [self.viewLabels addConstraint:[NSLayoutConstraint constraintWithItem:miBoton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.viewLabels attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[boton(22)]" options:0 metrics:nil views:@{@"boton":miBoton}]];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[boton(40)]" options:0 metrics:nil views:@{@"boton":miBoton}]];
    UILabel *miLabel = [[UILabel alloc] init];
    miLabel.translatesAutoresizingMaskIntoConstraints = NO;
    miLabel.backgroundColor = [UIColor redColor];
    miLabel.numberOfLines = 0;
    miLabel.text = @"ñkhnaermgñlkafmbñlkadnlñejtnhalrmvlkamnñnañorenoetñnngñsdbmnñgwn";
    [self.viewLabels addSubview:miLabel];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(8)-[label]-(10)-[boton]-(8)-|" options:0 metrics:nil views:@{@"label":miLabel,@"boton":miBoton}]];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previous]-(8)-[label]" options:0 metrics:nil views:@{@"label":miLabel,@"previous":self.labelFixed}]];
    UIView *pre = miLabel;
    miLabel = [[UILabel alloc] init];
    miLabel.translatesAutoresizingMaskIntoConstraints = NO;
    miLabel.backgroundColor = [UIColor greenColor];
    miLabel.numberOfLines = 0;
    miLabel.text = @"ñkhnaermgñlkafmbñlkadnlñejtnhalrmvlkamnñnañorenoetñnngñsdbmnñgwn";
    [self.viewLabels addSubview:miLabel];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(8)-[label]-(10)-[boton]-(8)-|" options:0 metrics:nil views:@{@"label":miLabel,@"boton":miBoton}]];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previous]-(8)-[label]" options:0 metrics:nil views:@{@"label":miLabel,@"previous":pre}]];
}

啊,在同一屏幕上,我在 IB 上添加了一个UILabelnumberOfLines设置为"0",垂直约束"大于或等于",水平约束以类似的方式设置(但两个约束都设置在 IB)...它工作得很好...这快把我逼疯了!!有什么帮助吗???

好吧,最后我发现了错误,正如我在"UPDATE 2"中所述,非常奇怪的是,它正在处理通过 IB 添加的UILabel,而不是动态添加的那些。

于是,我

再次查看了IB上的屏幕,发现我有一个具有"等于"关系的高度约束,当我将其更改为"大于或等于"时,一切都开始起作用!!!为什么我没有注意到约束不是"大于或等于"?因为当我添加带有标签的视图时,带有约束的视图变得越来越大。这是一个错误吗?

好吧,总结一下是否有人觉得这有用。"如何添加UILabel适应其内容的多行?"

iOS 6版本中,这是一个错误" preferredMaxLayoutWidth ",一旦"布局"就无法获得宽度,因此程序员必须通过代码进行设置(最好的解决方案是动态进行,例如我把它放在上面的问题上)。

幸运的是,在 iOS7 及更高版本上,此问题已得到解决,您只能在约束下依赖。

  1. 通过IB添加

    • 添加UILabel,并将"行数"设置为"0"(这将使UILabel根据需要使用任意数量的行)。
    • 添加约束,
    • 您至少需要 3 个约束(固定到顶部或底部,以及两侧),第四个是高度,关系"大于或等于"。
    • 请确保视图容器有足够的空间,或者还具有关系"大于或等于"的高度约束。
  2. 通过代码添加(只需粘贴上面发布的代码):

    UILabel *miLabel = [[UILabel alloc] init]; // Initialitze the UILabel
    miLabel.translatesAutoresizingMaskIntoConstraints = NO;  // Mandatory to disable old way of positioning
    miLabel.backgroundColor = [UIColor redColor];
    miLabel.numberOfLines = 0;  // Mandatory to allow multiline behaviour
    miLabel.text = @"ñkhnaergñsdbmnñgwn"; // big test to test
    [self.viewLabels addSubview:miLabel];  // Add subview to the parent view
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[label]-(10)-|" options:0 metrics:nil views:@{@"label":miLabel}]];  // horizontal constraint
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(10)-[label]" options:0 metrics:nil views:@{@"label":miLabel}]];
    UIView *pre = miLabel;
    // Adding a second label to test the separation
    miLabel = [[UILabel alloc] init];
    miLabel.translatesAutoresizingMaskIntoConstraints = NO;
    miLabel.backgroundColor = [UIColor greenColor];
    miLabel.numberOfLines = 0;
    miLabel.text = @"ñkhnaermgñlkafmbnoetñnngñsdbmnñgwn";
    [self.viewLabels addSubview:miLabel];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[label]-(10)-|" options:0 metrics:nil views:@{@"label":miLabel,@"boton":miBoton}]];
    [self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previous]-(10)-[label]" options:0 metrics:nil views:@{@"label":miLabel,@"previous":pre}]];
    

感谢那些试图帮助我的人!!这个网站是惊人的:)

当您

动态添加文本信息并且如果上述内容在UICollectionViewCellUITableViewCell上时,此答案将在编程情况下起作用。

在这些情况下,您需要更新contentView的布局和约束,然后设置 preferedWidth:

-(void) layoutSubviews
{
[super layoutSubviews];
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
//locationName.preferredMaxLayoutWidth = locationName.frame.size.width;
//locationDetails.preferredMaxLayoutWidth = locationDetails.frame.size.width;
[super layoutSubviews];
}

这类似于其他答案,其中您获取新的框架信息并将其用于首选MaxLayoutWidth,然后更新视图。

您需要向这些视图添加约束,以便垂直作为起点,UILabel需要它们的numberOfLines = 0

再三考虑,如果您的宽度以及高度发生变化,他在布局SubViews中preferredMaxLayoutWidth设置是糟糕的解决方案。最好在其他地方设置它,例如该单元格的初始化。在某些方面,如果您限制宽度,则可能不需要它。在我的情况下,这会导致每次设置该单元格时preferredMaxLayoutWidth都会发生变化。

最新更新