If you watch WWDC 2012 βBest Practices for Mastering Auto Layout,β they will guide you through this dynamic construction of layoutSubviews and / or constraints in layoutSubviews (about 45 minutes in the video). This focuses on adding views that are appropriate, but you can use it in your scenario as well.
The idea is that you have a subclass of UIView for your container view with all of these labels, and then override layoutSubviews to adjust the restrictions accordingly. It's a little hairy, but it works:
- (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 iterate through the `toRecipients`, and for each one, try adding // the label, see where constraints put it (using the label 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 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 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]; }
The key to dynamic height is the last limitation. When creating this subclass of UIView resize based on the constraints and internal size of the labels inside it, the height of this view will be determined by the labels inside it. When I add this subclassified view to my main view, I define it on the top, left and right to define the main view, but I leave the lower constraint undefined, and thus the last constraint above will determine the height. So, adding this subclass to my main view is as follows:
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;
Note. I set the background color to gray, so you can see that the height was dynamically set based on all the labels and their restrictions:
