AutoLayout Dynamic .xib View Height
This is my approach (Swift version):
Embed everything inside a view. Your xib hierarchy should be:
- Your xib
- View
- Everything else, your labels, buttons etc.
View's constraint should constraint to Top
, Leading
and Trailing
to superview
(your xib), and inside your "everything else", there should be an object's bottom
constraint to the superview (View).
In your code:
Load the nib
nib.frame.size.width = 80 // Set your desired width here
nib.label.text = "hello world" // Set your dynamic text here
nib.layoutIfNeeded() // This will calculate and set the heights accordingly
nib.frame.size.height = nib.view.frame.height + 16 // 16 is the total of top gap and bottom gap of auto layout
For my case, I'm loading this xib into collection view's cell, the xib's height is to be dynamically adjusted, and the width is to follow cell's width. Lastly, the above code block is inside my cellForItemAt
method of the collection view.
Hope it helps.
After much experimentation and reading, I have found the answer. When loading the .xib in some sort of constructor (in my case a class level convenience method), you must make sure to call [view setTranslatesAutoresizingMaskIntoConstraints:NO];
For example, I've done the following:
+ (InputView *)inputViewWithHeader:(NSString *)header subHeader:(NSString *)subHeader inputValidation:(ValidationBlock)validation
{
InputView *inputView = [[[NSBundle mainBundle] loadNibNamed:@"InputView" owner:self options:nil] lastObject];
if ([inputView isKindOfClass:[InputView class]]) {
[inputView setTranslatesAutoresizingMaskIntoConstraints:NO];
[inputView configureWithHeader:header subHeader:subHeader inputValidation:validation];
[inputView layoutIfNeeded];
[inputView invalidateIntrinsicContentSize];
return inputView;
}
return nil;
}
Then, it's necessary to override layoutSubviews
and intrinsicContentSize
. Overriding layoutSubviews
allows me to set the preferredMaxLayoutWidth
of my label, while overriding intrinsicContentSize
allows me to calculate the size based on constraints and subviews! Here is my implementation of those:
- (void)layoutSubviews {
[super layoutSubviews];
self.subHeaderLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
[super layoutSubviews];
}
- (CGSize)intrinsicContentSize {
CGFloat height = self.headerView.bounds.size.height;
height += self.headerInputSpacer.constant;
height += self.inputField.bounds.size.height;
height += self.inputButtonSpacer.constant;
height += self.buttonView.bounds.size.height;
CGSize size = CGSizeMake([UIScreen mainScreen].bounds.size.width - 20, height);
return size;
}
I'm sure there are ways to improve this, or better ways to make it happen, but for now it is at least sized correctly! Very useful for views that should not have user-defined frames.