iOS AutoLayout越来越多

时间:2013-08-19 15:22:10

标签: ios autolayout

我对自动布局有点新意,并且到了我现在被困住的地步。我今天搜索了WWDC视频,因为我记得一个例子,却找不到它......

简单来说,布局应该是这样的:

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

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

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

我对价值标签有什么限制?

1 个答案:

答案 0 :(得分: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];

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

screen snapshot