ActivateConstraints: and deactivateConstraints: not saved after rotation for constraints created in IB

The new NSLayoutConstraint activateConstraints: and deactivateConstraints: methods do not work correctly with the restrictions created by IB (they work correctly for the restrictions created by the code). I created a simple one-button test application that has two sets of limitations. One installed set has restrictions on the center and center, and another set that is deleted has upper and left restrictions (constant 10). The button method toggles these sets of constraints. 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 application starts, the button is in the correct, centered position, determined by the established restrictions. After I activate / inactivate in the button's action method, the button will return to its new position correctly, but when I rotate the view to landscape, it will return to its originally defined position (although the newly activated set still appears in the log as being active). When I turn back to the portrait, the button remains in its original position (in the center of the screen), and now the log shows that the initial set of restrictions is active and those that I activated are inactive.

The question is, is this a mistake or should these methods not work this way with certain IB restrictions?

+24
ios cocoa-touch autolayout nslayoutconstraint
Dec 27 '14 at 0:14
source share
3 answers

The problem is that you are doing something inconsistent with the "undefined" restrictions in the storyboard. They are there, but not there. Uninstalled restrictions are for size classes only! You use them if you decide that the restrictions on Xcode conversion will be automatically set during rotation. Xcode cannot handle what you are doing. But if you create a second set of restrictions in the code, everything will work fine.

So do it. Remove the two “uninstalled” restrictions and remove the uninstalledConstraints output. Now replace all view controller code as follows:

 @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 press the button a few times. As you can see, he jumps between two positions. Now rotate the application; the button remains where it is.

+22
Dec 27 '14 at 2:58
source share

I faced a similar situation.

I created two sets of NSLayoutConstraints in an interface builder. Each set for one occasion. One set was “installed” by another.

When I switch case, the corresponding set of layout constraints was activated and the other was disabled. As described in Qusteion, rotating forward and backward does not work properly.

This is solved by installing both sets in the interface builder. And to get rid of the warnings, I used the lower priority slighlt (999) for the second set. It worked for me.

Btw: Strang I used the “installed / not installed” aproach on another view controller, their work worked.

In the non-operational case, the viewcontroller was built into the contenterview, perhaps for this reason.

+9
Aug 20 '15 at 15:41
source share

You can use this workflow, although it’s clear that this is not a supported use case currently for people at Apple.

The trick is not to remove the constraints (as @matt pointed out), but to deactivate them in the viewDidLoad() your UIViewController subclass before the layout occurs (and your conflicting constraints cause a problem).

Now I use this method and it works great. Ideally, of course, wed have the ability to visually create groups of constraints and use them as a keyframe, simply activating and deactivating them (with free animation between them) :)

+2
Nov 13 '15 at 19:59
source share



All Articles