Search code examples
iosautolayoutinterface-buildernslayoutconstraintsize-classes

Can't change Constraint IBOutlet that is defined for different size classes in IB


I made a special test app for this case. (I'm sorry it is already removed)

I added a view on my controller's view in Storyboard, set up AutoLayout constraints in Interface Builder and made one of them (vertical space) is defferent for different size classes. Screenshot from IB

So the value is 100 for Any height, Any width and 0 for Regular height, Regular width. It works well, on iPhone vertical distance from top is 100, when on iPad it is 0.

Also I made IBOutlet for this constraint and want to change it in runtime to 10

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

it seemed I couldn't change it because it gives no effect

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.topVerticalConstraint.constant = 10; // it doesn't work
}

Although it works when I remove value for Regular height, Regular width in Interface Builder.

Am I miss something about the size classes?


Solution

  • The problem is that constraints are not fully defined yet until Layout events happen between -viewWillLayoutSubviews and -viewDidLayoutSubviews where all the parameters from IB comes into play. My rule of thumb is:

    • if you use frames to position your views manually you can do it as early as -viewDidLoad,
    • if you use autolayout constraints for positioning, make adjustments as early as -viewDidLayoutSubviews;

    The second statements only considers code adjustments to constraints that have been made in IB. Adjustments that you are making in -viewDidLoad will be overridden by parameters set in IB during layout. If you add constraints with code you can set them in -viewDidLoad, since there will be nothing to override them.

    I've changed your code a bit and it works:

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *topVerticalConstraint;
    @property (weak, nonatomic) IBOutlet UIView *square;
    
    @property (assign, nonatomic) BOOL firstLayout;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    
    {
        [super viewDidLoad];
    
        self.firstLayout = YES;
    }
    
    - (void)viewDidLayoutSubviews {
    
        [super viewDidLayoutSubviews];
    
        if (self.firstLayout) {
    
            self.topVerticalConstraint.constant = 10;
            self.firstLayout = NO;
    
        }
    }
    
    @end
    

    Notice that -viewDidLayoutSubviews is called many times during the lifetime of a ViewController, so you have to make sure that your adjustments happen only once on initial load.