4

我对自动布局有点陌生,到了现在陷入困境的地步。我今天搜索了 WWDC 视频,因为我记得一个例子但找不到它......

在它的简单形式中,布局应该是这样的:

@"|[label]-[value1]-[value2]-[hideDetailsButton]|"
@"V:|[label]|"

但是应该可以有多个值,其中 label 和 hideDetailsButton 保持顶部对齐并且值增长到多行。像这样:

@"|[label]-[value1]-[value2]-[hideDetailsButton]|"
@"|[value3]-[value4]-[value5]"
@"V:|[label]-[value3]|" 

值标签需要什么限制?

4

1 回答 1

4

如果您观看 WWDC 2012 Best Practices for Mastering Auto Layout,它们将引导您完成子视图和/或约束的动态构建layoutSubviews(视频大约 45 分钟)。这侧重于添加适合的视图,但您也可以在您的场景中使用它。

这个想法是你UIView的容器视图有一个包含所有这些标签的子类,然后覆盖layoutSubviews以适当地配置约束。它有点毛茸茸,但它有效:

- (void)layoutSubviews
{
    [super layoutSubviews];

    // add any labels for my `toRecipients` (and add to my dictionary that keeps
    // track of which `UILabel` is for which `toRecipient`)

    for (NSString *toRecipient in self.toRecipients)
    {
        UILabel *label = self.toLabels[toRecipient];
        if (!label)
        {
            label = [[UILabel alloc] init];
            label.text = toRecipient;
            label.translatesAutoresizingMaskIntoConstraints = NO;
            [self addSubview:label];
            [self.toLabels setObject:label forKey:toRecipient];
        }
    }

    // remove any existing constraints on subviews (you could keep track of them and
    // modify them, but I find it just as easy as to start from scratch every time)

    NSMutableArray *constraintsToRemove = [NSMutableArray array];
    for (NSLayoutConstraint *constraint in self.constraints)
    {
        if ([constraint.firstItem superview] == self || [constraint.secondItem superview] == self) {
            [constraintsToRemove addObject:constraint];
        }
    }
    [self removeConstraints:constraintsToRemove];

    // add initial constraints for that leading "To:" label, putting it in the upper left corner

    NSDictionary *views = @{@"to" : self.toLabel};
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[to]" options:0 metrics:nil views:views]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[to]" options:0 metrics:nil views:views]];

    // now let's iterate through the `toRecipients`, and for each one, try adding
    // the label, see where constraints put it (using the label's intrinsic size, too),
    // and if it was too wide, then remove those constraints and add new constraints
    // to put it on the next line

    UIView *previousView = self.toLabel;
    for (NSString *toRecipient in self.toRecipients)
    {
        UIView *nextView = self.toLabels[toRecipient];
        views = NSDictionaryOfVariableBindings(previousView, nextView);
        NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[previousView]-[nextView]" options:0 metrics:nil views:views];
        [self addConstraints:constraints];
        [self updateConstraintsIfNeeded];
        [super layoutSubviews];
        if (CGRectGetMaxX(nextView.frame) < CGRectGetMaxX(self.bounds))
        {
            // if there was room, let's also set the baseline to be the same as the previous item

            [self addConstraint:[NSLayoutConstraint constraintWithItem:nextView attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual toItem:previousView attribute:NSLayoutAttributeBaseline multiplier:1.0 constant:0.0]];
        }
        else
        {
            // otherwise, let's get rid of the constraints I just added and move this next item down to the next line

            [self removeConstraints:constraints];
            [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[nextView]" options:0 metrics:nil views:views]];
            [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previousView]-[nextView]" options:0 metrics:nil views:views]];
        }

        previousView = nextView;
    }

    // set the bottom constraint for the last recipient

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[nextView]-|" options:0 metrics:nil views:views]];
    [self updateConstraintsIfNeeded];
    [super layoutSubviews];
}

动态高度的关键是最后一个约束。在使这个UIView子类根据其内部标签的约束和固有大小调整自身大小时,该视图的高度将由其中的标签定义。当我将此子类视图添加到我的主视图时,我将其定义为由主视图定义的顶部、左侧和右侧,但我未定义底部约束,因此上面的最后一个约束将定义高度。因此,将此子类添加到我的主视图中如下所示:

RecipientsView *recipientsView = [[RecipientsView alloc] init];
recipientsView.toRecipients = @[@"rob@frankfurt.de", @"r@berlin.de", @"frank@dusseldorf.de", @"ernest@munich.de", @"mo@cologne.de", @"curly@stuttgart.de"];
recipientsView.backgroundColor = [UIColor lightGrayColor];
recipientsView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:recipientsView];
self.recipientsView = recipientsView;

// set the left, right, and top constraints to the main view, but I'll let the
// intrinsic size of the labels dictate the height of this UIView subclass, RecipientsView

NSDictionary *views = NSDictionaryOfVariableBindings(recipientsView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[recipientsView]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[recipientsView]" options:0 metrics:nil views:views]];

[self.view layoutIfNeeded];

请注意,我将背景颜色设置为浅灰色,因此您可以看到高度是根据所有标签及其约束动态设置的:

屏幕快照

于 2013-08-20T03:39:38.170 回答