使用自动布局以编程方式定位在 UIView 中动态添加的 UILabel

Programmatically position UILabels added dynamically inside UIView with autolayout

我有一组项目要显示在我的 UITableViewCell 中。这些项目中的每一个都将使用 UILabel 动态显示。我使用自动布局来设置视图布局。

这是我 tableViewCell 的布局方式:

+------------------------------------------------+
| [cellView]                                     |
| +---------------------------------------------+|
| |[otherView] <- fixed height                  ||
| | +------------------------------------------+||
| | |[UILabel]                                 |||
| | +------------------------------------------+||
| +---------------------------------------------+|
| +---------------------------------------------+|
| |[itemView]                                   ||
| | +---------------------------+ +------------+||
| | |[itemLabel]                |-|[priceLabel]|||
| | +---------------------------+ +------------+||
| | +---------------------------+ +------------+||
| | |[itemLabel]                |-|[priceLabel]|||
| | +---------------------------+ +------------+||                                            
| |                                             ||
| |     --Add the next UILabels dynamically--   ||
| +---------------------------------------------+|
+------------------------------------------------+   

为清楚起见


在我的 cellForRowAtIndexPath 中,我使用 constraintsWithVisualFormat:

设置了 itemLabel & priceLabel 约束
for (NSDictionary *item in items) {
    UILabel *itemLabel = [[UILabel alloc] init];
    itemLabel.translatesAutoresizingMaskIntoConstraints = NO;
    itemLabel.font = [UIFont systemFontOfSize:14.0f];
    itemLabel.backgroundColor = [UIColor yellowColor];
    itemLabel.text = [item valueForKey:@"item_name"];
    [cell.itemView addSubview:itemLabel];
    
    UILabel *priceLabel = [[UILabel alloc] init];
    priceLabel.translatesAutoresizingMaskIntoConstraints = NO;
    priceLabel.font = [UIFont systemFontOfSize:14.0f];
    priceLabel.textAlignment = NSTextAlignmentRight;
    priceLabel.backgroundColor = [UIColor greenColor];
    priceLabel.text = [NSString stringWithFormat:@"RM %@", [item valueForKey:@"price"]];
    [cell.itemView addSubview:priceLabel];
    
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(cell.itemView, itemLabel, priceLabel);
    [cell.itemView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[itemLabel]-5-[priceLabel(70)]|"
                                                                         options:0
                                                                         metrics:nil
                                                                           views:viewsDictionary]];
    [cell.itemView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[itemLabel]|"
                                                                          options:0
                                                                          metrics:nil
                                                                            views:viewsDictionary]];
    [cell.itemView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[priceLabel]|"
                                                                          options:0
                                                                          metrics:nil
                                                                            views:viewsDictionary]];


}

最终结果如下。唯一的问题是创建的 itemLabelpriceLabel 相互重叠,而不是从上到下很好地对齐。

结果:

查看调试:

如何正确设置约束,以便 itemLabelpriceLabel 从上到下很好地对齐,并且 itemView 会根据其中的项目数量调整大小?

您没有足够的限制。问题是您没有在一个 itemLabelprevious itemLabel 之间设置约束。当你循环时,你需要跟踪上一个项目标签,这样你就可以 space 从它向下一个。

换句话说:你要找的技术是三种情况:

  • 第一个标签。它固定在它上面的任何地方(可能是超级视图的顶部)。

  • 除最后一个之外的所有其他项目标签。每个都固定到前一个标签。

  • 最后一个标签。它被固定到前一个标签 和底部 ,从而赋予单元格高度。

巧合的是,我手头有一些代码可以说明这个原理;这种情况显然与你的情况不是 100% 相同,但原则正是你想要遵循的那种事情:

UILabel* previousLab = nil;
for (int i=0; i<30; i++) {
    UILabel* lab = [UILabel new];
    // lab.backgroundColor = [UIColor redColor];
    lab.translatesAutoresizingMaskIntoConstraints = NO;
    lab.text = [NSString stringWithFormat:@"This is label %d", i+1];
    [sv addSubview:lab];
    [sv addConstraints:
     [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[lab]"
                                             options:0 metrics:nil
                                               views:@{@"lab":lab}]];
    if (!previousLab) { // first one, pin to top
        [sv addConstraints:
         [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(10)-[lab]"
                                                 options:0 metrics:nil
                                                   views:@{@"lab":lab}]];
    } else { // all others, pin to previous
        [sv addConstraints:
         [NSLayoutConstraint
          constraintsWithVisualFormat:@"V:[prev]-(10)-[lab]"
          options:0 metrics:nil
          views:@{@"lab":lab, @"prev":previousLab}]];
    }
    previousLab = lab;
}

// last one, pin to bottom, this dictates content size height
[sv addConstraints:
 [NSLayoutConstraint constraintsWithVisualFormat:@"V:[lab]-(10)-|"
                                         options:0 metrics:nil
                                           views:@{@"lab":previousLab}]];