如果您观看 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];
请注意,我将背景颜色设置为浅灰色,因此您可以看到高度是根据所有标签及其约束动态设置的: