15

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.

2
  • Are you calling layoutIfNeeded after you make the changes to the constraints? Could you post the code where you change the width and height? Also, if the view has a height and a width constraints in the storyboard, adding the constraints to the contentView will make the constraints crash, because they will be conflicting. Commented Mar 23, 2015 at 8:42
  • @CatalinaT. Thank you for your answer. I didn't call layoutIfNeeded, but I have now tried with it, it doesn't make any change. _vContainer is a dummy view which is in center of my screen. My idea is to add my CustomView to that dummy view and that my CustomView has the same size like dummy view. That's why I wrote 4 constraints above where I am saying that my CustomView should be aligned with all four sides of dummy view. Indirectly, I am setting CustomView's width and height in this way. But, no luck. CustomView has always the same size from it's XIB (150 x 50). Commented Mar 23, 2015 at 8:55

3 Answers 3

19

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.

Sign up to request clarification or add additional context in comments.

7 Comments

I wrote _vCustomView.translatesAutoresizingMaskIntoConstraints = NO; before I added my CustomView into dummy container. Isn't that enough so that I can use autolayout constraints on my CustomView object programmatically? Afterwards, I tried with these 4 constraints to make my CustomView fit the dummy container view, but as said - no luck. I have tried to call your method after adding my CustomView object to dummy container and setting it's four constraints, but no luck - same result. CustomView is still 150 x 50 and not as big as dummy container (like I think it should be).
I'm talking about that method ‘- (void)setup‘ after adding the xib as a subview call the one that I've posted. It's your xib view that is missing constraints.
Are you sure that you set translatesAutoresizingMaskIntoConstraints of your xib view to NO before adding it? because I've added later after an edit
I'll be damned. You're ninja. It works. Many thanks. This line you have added in your edit has brought everything to life. I can now resize my CustomView programmatically with constraints from my view controller. Thanks once more!
This answer might work as expected, but changing self.bounds = self.view.bounds; to view.frame = self.bounds in your custom XIB, would have made the same effect, without doing anything else. Here's a video that illustrates the idea, in Swift youtube.com/watch?v=H-55qZYc9qI
|
3

Swift 4.0 function to stretch to super view:

Code Snippet:

static public func stretchToSuperView(view:UIView)
    {
        view.translatesAutoresizingMaskIntoConstraints = false
        var d = Dictionary<String,UIView>()
        d["view"] = view
        for axis in ["H","V"] {
            let format = "\(axis):|[view]|"
            let constraints = NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(rawValue: 0), metrics: [:], views: d)
            view.superview?.addConstraints(constraints)
        }
    }

Note: Just call the function with an argument as your subview.

This helps me to resolve the .xib resing issue in swift 4.0.

Comments

1

In your code you should have something like this:

First, a IBOutlet to the width constraint you wanna change, either in the viewController or in your CustomView (I don't really know where you have the logic to calculate the constraint value).

@property (nonatomic, weak) IBOutlet NSLayoutConstraint *widthConstraint;

Then, in the method you wanna update the constant value, you'll have something like this:

-(void)updateWidth {
    [self.widthConstraint setConstant:newConstant];
    [self.view layoutIfNeeded]; // self.view must be an ancestor of the view

    //If you need the changes animated, call layoutIfNeeded in an animation block
}

Hope this works for you. Good luck!

1 Comment

Andrea helped me with his answer, but many thanks to you also for willingness to help. Best regards.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.