0
votes

How do I set up a height constraint for a superview whereby the superview's height is the total height of its subviews, and its subview can be a collections of multiline UILabel (which can change in height depending on font size or how many lines of texts are being shown).

1

1 Answers

1
votes

you do it by constraining the first label's top to self's top and then the last label's bottom to self's bottom. each of the labels in between have their top constrained to the previous label's bottom.

@interface SomeView: UIView
@end

@implementation SomeView

- (instancetype)init {
    self = [super init];
    if (!self)
        return nil;

    // create an array of random labels
    NSArray <UILabel*> *labels = [self labelsWithRandomText:10];

    // add each label to the view, setting it's top to the previous view's bottom
    UIView *previousView = self;
    for (UILabel *label in labels) {
        [self addSubview:label];

        // bound the left and right of the label to match the parent view
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[label]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(label)]];

        // if this is the first label being added, set its top to self's top
        // otherwise set its top to the bottom of the previous view
        NSLayoutAttribute previousAttribute = previousView == self ? NSLayoutAttributeTop : NSLayoutAttributeBottom;
        [self addConstraint:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:previousView attribute:previousAttribute multiplier:1 constant:0]];

        // update previous view
        previousView = label;
    }

    // constrain self's bottom to the bottom of the last label
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:previousView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];

    return self;
}

// create an array of labels w/ random color and background text
- (NSArray <UILabel *> *)labelsWithRandomText:(int)labelCount {
    NSMutableArray <UILabel*> *labels = [NSMutableArray new];
    for (int i = 0; i < labelCount; i++) {
        UILabel *label = [UILabel new];
        label.translatesAutoresizingMaskIntoConstraints = NO;
        label.numberOfLines = 0;
        label.backgroundColor = [self randomColor];
        label.text = [self randomString];
        [labels addObject:label];
    }

    return labels;
}

// create random color
- (UIColor *)randomColor {
    return [UIColor colorWithRed:(arc4random()%255)/255. green:(arc4random()%255)/255. blue:(arc4random()%255)/255. alpha:1];
}

// create random string
- (NSString *)randomString {
    static NSArray <NSString*> *words;
    static NSInteger wordCount;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        words = @[@"cat ", @"dog ", @"bird ", @"went ", @"home ", @"with ", @"under ", @"red ", @"blue ", @"green "];
        wordCount = [words count];
    });

    NSMutableString *randomString = [NSMutableString new];

    // create somewhere between 10-30 words
    int randomWordCount = arc4random() % 20 + 10;
    for (int i = 0; i < randomWordCount; i++) {
        [randomString appendString:words[arc4random() % wordCount]];
    }
    return randomString;
}

@end