Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically resize custom UIView from XIB

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 constraints
  • by using initWithFrame 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.

like image 491
uerceg Avatar asked Mar 23 '15 08:03

uerceg


People also ask

How to add a custom UIView to an xib file?

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.

How to add a UIView to nibview?

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.

How do I add a UIView to an IB file?

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.

Is it possible to remove Xib from a view class?

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.


1 Answers

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:

  • Change the autoresizing masks of your xib view to flexible width and height but they need to match the parent size or you will not have a full cover
  • Add some constraints that constraint the two view to change accordingly to the parent changes. And you achieve that saying between the XIB view and its parent view the space between trailing/leading and top/botton is 0. Is like you are putting some glue on their borders. To do that you need to set the autotranslate resizing mask into constraint to NO, or you can have some conflicts as you posted in the log.

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.

like image 121
Andrea Avatar answered Oct 13 '22 12:10

Andrea