I've not been able to find much in the way of AutoLayout with individual .xib files...
I've got a standalone .xib file that has 3 views - a header view (which contains two labels), an input, and a footer (which contains two buttons). It looks like this:
The labels in the header view have constraints which should affect the vertical size of the header view, and in turn the size of the entire view. The subheader is a label with 0 lines, which means it is multi-line and dynamic. Everything else has a set height with horizontal constraints to superview and top constraints to sibling (or superview in header view's case).
The issue I am having is that when I load this .xib file in code for display, the height is always static based on what is defined in Xcode's inspectors. Is it possible to make the height of the entire view dynamic based on the width (which affects dynamic label height and therefore rest of the view)?
For example - if I load this view from the .xib and set its width to 300, how do I then have it resize its height to accommodate the dynamic label's new height? Do I need to use the intrinsicContentSize
method to define this size?
Add a dummy UITextView height constraint with default height, connect it with IBOutlet ( TextViewHeightConstraint ). When you set your UITextView’s text asynchronously you should calculate the height of UITextView and set UITextView’s height constraint to it.
This lightweight class represents a rectangular frame that can participate in Auto Layout constraints. Layout guides do not have a graphic context, and they are not part of the view hierarchy. This makes layout guides ideal for grouping items or defining white space.
Unfortunately, you cannot add layout guides to a scene in Interface Builder, and mixing programmatically created objects with a storyboard-based scene can become quite complex. As a general rule of thumb, it’s better to use storyboards and Interface Builder, than to use custom layout guides.
By default, Interface Builder should set the label’s content hugging to 251, and the text field to 250. You can verify this in the Size inspector. Notice that this layout only uses two constraints (4 and 5) to define the vertical layout, and three constraints (1, 2, and 3) to define the horizontal layout.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With