I have following problem: I have created a custom UIView
class and its layout in XIB
file. Let's say that size of my custom view in XIB is 150 x 50. I have enabled sizeClasses
(wAny hAny) and AutoLayout
. In Simulated Metrics I have set Size = Freeform
, Status Bar = None
, all other = Inferred
.
Code from my custom UIView
class looks like this:
#import "CustomView.h"
@implementation CustomView
#pragma mark - Object lifecycle
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setup];
}
return self;
}
#pragma mark - Private & helper methods
- (void)setup {
if (self.subviews.count == 0) {
[[NSBundle mainBundle] loadNibNamed:@"CustomView" owner:self options:nil];
self.bounds = self.view.bounds;
[self addSubview:self.view];
}
}
@end
In my UIViewController
where I want to add this custom UIView
, I have made dummy UIView (which size I have set in Interface Builder) where I want to add my CustomView
and I would like that my CustomView
fits the bounds of my container view.
I am trying to do that like this:
_vCustomView = [[CustomView alloc] init];
_vCustomView.translatesAutoresizingMaskIntoConstraints = NO;
[_vContainer addSubview:_vCustomView];
[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0]];
[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0]];
[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[_vContainer addConstraint:[NSLayoutConstraint constraintWithItem:_vCustomView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_vContainer attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]];
But no luck. Let's say that my container view is 300 x 100, when I add my custom UIView
to it, my custom view is still 150 x 50.
I have tried to work without container view - to add my custom UIView
object directly to self.view of my UIViewController
and set it's width and height with:
autoLayout
constraintsinitWithFrame
when initializing my custom UIView
but again - no luck. Custom view is always 150 x 50.
Can someone explain to me how can I programmatically add my custom UIView
made in XIB
and resize it using autoLayout
constraints?
Many thanks in advance.
Edit #1: Error I get when trying to run Andrea's code on my CustomView
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x17489b760 V:[UIView:0x17418d820(91)]>",
"<NSLayoutConstraint:0x170c9bf80 V:|-(0)-[CustomView:0x1741da7c0] (Names: '|':CustomView:0x1741da8b0 )>",
"<NSLayoutConstraint:0x170c9bfd0 V:[CustomView:0x1741da7c0]-(0)-| (Names: '|':CustomView:0x1741da8b0 )>",
"<NSLayoutConstraint:0x170c9be90 V:|-(0)-[CustomView:0x1741da8b0] (Names: '|':UIView:0x17418d820 )>",
"<NSLayoutConstraint:0x170c9bee0 CustomView:0x1741da8b0.bottom == UIView:0x17418d820.bottom>",
"<NSAutoresizingMaskLayoutConstraint:0x170c9dba0 h=--& v=--& CustomView:0x1741da7c0.midY == + 25.0>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x170c9bfd0 V:[CustomView:0x1741da7c0]-(0)-| (Names: '|':CustomView:0x1741da8b0 )>
Edit #2: It works!
After @Andrea added:
view.translatesAutoresizingMaskIntoConstraints = NO;
in his:
- (void) stretchToSuperView:(UIView*) view;
method, everything works like I expected.
Thanks to @Andrea.
Then, navigate to your XIB file, select File’s Owner from the left sidebar, and add your custom UIView as its custom class. Now, go to your UIViewController, add a UIView component and add your UIView as a Custom Class.
The last step would be to create a custom UIView and a XIB file that will inherit from the NibView superclass. Then, navigate to your XIB file, select File’s Owner from the left sidebar, and add your custom UIView as its custom class. Now, go to your UIViewController, add a UIView component and add your UIView as a Custom Class.
Add Entire View as an IB Outlet in UIView File Now that you’ve added your custom UIView as the file’s owner, you should be able to tap the assistant editor button in the upper right, and load the view side by side with your XIB. Ctrl-drag the entire view in, as an outlet to your view file.
It is possible to leave out all the xib loading mechanism from the view instance. We can create a set of extensions in order to have a nice view loader with a custom view class from a xib file.
I guess that the main issue is that you do not use auto layout when you add your xib into the content view.
After adding the subview in you init methods please call that method, passing the view of the xib:
- (void) stretchToSuperView:(UIView*) view {
view.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *bindings = NSDictionaryOfVariableBindings(view);
NSString *formatTemplate = @"%@:|[view]|";
for (NSString * axis in @[@"H",@"V"]) {
NSString * format = [NSString stringWithFormat:formatTemplate,axis];
NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:bindings];
[view.superview addConstraints:constraints];
}
}
[EDIT]
Your setup code should look like that:
- (void)setup {
if (self.subviews.count == 0) {
[[NSBundle mainBundle] loadNibNamed:@"CustomView" owner:self options:nil];
self.bounds = self.view.bounds;
[self addSubview:self.view];
[self stretchToSuperView:self.view];
}
}
Pay attention that inside stretch view I've added view.translatesAutoresizingMaskIntoConstraints = NO;
[EDIT 2]
I'll try to elaborate my answer as requested. Using autolayout, when you add a view without setting constraints autolayout automatically converts autoresizing masks into constraints the default property of them is UIViewAutoresizingNone
that means to do not autoresize.
Your XIB view was added to its parent without constraints and without the possibility of autoresize thus keeping its original size. Since you want that your view to resize accordingly to your view you had two choices:
What you are doing later is adding constraints to the view (that hosts the XIB view) to its superview, but there were no constraints between the XIB and its parent, thus autolayout doesn't know how to resize the XIB view.
Each views in a view hierarchy should have its own constraints.
Hope this helps to understand better.
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