The new NSLayoutConstraint
methods activateConstraints:
and deactivateConstraints:
don't appear to work correctly with IB-created constraints (they do work correctly for code-created constraints). I created a simple test app with one button that has two sets of constraints. One set, which is installed, has centerX and centerY constraints, and the other set, which is uninstalled, has top and left constraints (constant 10). The button method switches these constraints sets. Here is the code,
@interface ViewController () @property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *uninstalledConstraints; @property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *installedConstraints; @end @implementation ViewController - (IBAction)switchconstraints:(UIButton *)sender { [NSLayoutConstraint deactivateConstraints:self.installedConstraints]; [NSLayoutConstraint activateConstraints:self.uninstalledConstraints]; } -(void)viewWillLayoutSubviews { NSLog(@"installed: %@ uninstalled: %@", ((NSLayoutConstraint *)self.installedConstraints[0]).active ? @"Active" : @"Inactive", ((NSLayoutConstraint *)self.uninstalledConstraints[0]).active ? @"Active" : @"Inactive"); }
When the app launches, the button is in the correct, centered position defined by its installed constraints. After I do the activation/inactivation in the button's action method, the button moves to its new position correctly, but when I rotate the view to landscape, it moves back to its initially defined position (though a log still shows the newly activated set as being active). When I rotate back to portrait, the button stays in its initial position (centered in the screen), and now the log shows that initial set of constraints as active, and the ones I activated, as inactive.
The question is, is this a bug, or are these methods not supposed to work in this way with IB defined constraints?
The problem is that you are doing something incoherent with "uninstalled" constraints in the storyboard. They are there but not there. "Uninstalled" constraints are for use only with size classes! You use them if you are going to let Xcode swap constraints for you automatically on rotation. Xcode can't cope with what you're doing. But if you create the second set of constraints in code, everything will work fine.
So, do this. Delete the two "uninstalled" constraints, and delete the uninstalledConstraints
outlet. Now replace your entire view controller code with this:
@property (strong, nonatomic) NSMutableArray *c1; @property (strong, nonatomic) NSMutableArray *c2; @property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *installedConstraints; @property (weak,nonatomic) IBOutlet UIButton *button; @end @implementation ViewController { BOOL did; } - (void)viewDidLayoutSubviews { NSLog(@"did"); if (!did) { did = YES; self.c1 = [self.installedConstraints mutableCopy]; self.c2 = [NSMutableArray new]; [self.c2 addObject: [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTopMargin multiplier:1 constant:30]]; [self.c2 addObject: [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeadingMargin multiplier:1 constant:30]]; } } - (IBAction)switchconstraints:(UIButton *)sender { [NSLayoutConstraint deactivateConstraints:self.c1]; [NSLayoutConstraint activateConstraints:self.c2]; NSMutableArray* temp = self.c1; self.c1 = self.c2; self.c2 = temp; }
Now repeatedly press the button. As you see, it jumps between the two positions. Now rotate the app; the button stays where it is.
I faced a similar situation.
I created two sets of NSLayoutConstraints in interface builder. Each set for one case. One set was "installed" the other not.
When I switch the case the coresponding set of layout constraint was activated and the other was deacticated. An as described in the Qusteion rotating forth and back does not work properly.
Is solved this by installing both sets in interface builder. And to get rid of the warnings I used a slighlt lower priority (999) for the second set. This worked for me.
Btw: Strang i used the "installed/not installed" aproach on another viewcontroller, their it worked.
In the not working case the viewcontroller was embedded in a containerview, perhaps that was the reason.
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