Cannot modify Constraint IBOutlet that is defined for different size classes in IB

I made a special test application for this case. (I'm sorry it has already been deleted)

I added an idea of ​​my controller view in Storyboard , set AutoLayout constraints in Interface Builder and made one of them (vertical space) for different difference 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 the iPhone the vertical distance from the top is 100, when on the iPad it's 0.

I also made an IBOutlet for this restriction and want to change it at runtime to 10

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

it seemed I could not change it because it has no effect

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

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

Am I missing something in size classes?

+6
source share
6 answers

The problem is that restrictions are not yet fully defined until layout events occur between -viewWillLayoutSubviews and -viewDidLayoutSubviews , where all parameters from IB will be involved. My rule of thumb is:

  • If you use frames to place your views manually, you can do this already before -viewDidLoad,
  • if you use autodetection restrictions for positioning, make adjustments also -viewDidLayoutSubviews;

The second statements only take into account code adjustments for restrictions that were made in IB. The adjustments you make to -viewDidLoad will be overridden by the options set in IB during the layout. If you add restrictions with the code, you can set them to -viewDidLoad, since there will be nothing to override them.

I changed my 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 

Note that -viewDidLayoutSubviews is called many times during the lifetime of the ViewController, so you need to make sure that your adjustments happen only once at boot time.

+12
source

Problem:

If you set a different value for different size classes in IB for a restriction like this:

enter image description here

then you cannot change the constant value in the code as follows:

 self.adHeightConstraint.constant = 0; // value set to 0 [self.view layoutIfNeeded]; // value get back to IB value (44 or 36) 

In this situation, you can see that your constant value is retained only until the views are recounted. So, after [self.view layoutIfNeeded] value of the reset constant will return to everything that was set in IB.

Decision:

  • Add the second restriction of the same attribute (in my case it was height) with the desired value. You can set this value in IB or change it in the code.
  • Set a low priority for this new restriction. Since this is a low priority, it will not be a conflict.
  • Now that you need to apply the new constant, just disable the first restriction :

     self.adHeightConstraint.active = NO; [self.view layoutIfNeeded]; 
+3
source

I am having the same problem, but it seems to have nothing to do with viewDidLoad and viewDidLayoutSubviews. The Builder interface can manage alternative constraint constants for different size classes, but when you try to update NSLayoutConstraint.constant in code, this constant is not associated with any specific size class (including the active one).

From Apple Docs Changing Constraint Constants for a Size Class (Xcode 7, Interface Builder) Xcode 7 row-wise limitations for multiple class classes

My solution was to remove the alternative constants from IB and control the variable constants of the size constraint in the code, only for those specific constraints that are updated / changed in the code. Any constraints that are controlled only by the / IB storyboard can use alternate-size constants as usual.

 // XCode 7.0.1, Swift 2.0 static var isCompactHeight : Bool = false; static var heightOffset : CGFloat { return (isCompactHeight ? compactHeightOffset : regularHeightOffset); } /* applyTheme can be called as early as viewDidLoad */ func applyTheme() { // This part could go wherever you handle orientation changes let appDelegate = UIApplication.sharedApplication().delegate; let window = appDelegate?.window; let verticalSizeClass = window??.traitCollection.verticalSizeClass ?? UIUserInterfaceSizeClass.Unspecified; isCompactHeight = (verticalSizeClass == UIUserInterfaceSizeClass.Compact); // Use heightOffset changingConstraint.constant = heightOffset; } 

I hope some later version of Swift / Xcode introduces getters and setters that take into account dimensional alternatives, reflecting the functionality that is already available through IB.

+1
source

I am checking the same scenario in the example project that it worked, maybe you forgot to connect the NSLayoutConstraint topVerticalConstraint to the storyboard.

0
source

Change constraint constant in viewDidLayoutSubviews

 - (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; if (IS_IPHONE4) { self.topConstraint.constant = 10; self.bottomButtonTop.constant = 10; self.pageControlTopConstraint.constant = 5; } } 
0
source

The simplest solution:

 if (self.view.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassRegular && self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) { // for iPhone cnicTopConstraint.constant = -60; } else { // for iPad cnicTopConstraint.constant = -120; } 
0
source

Source: https://habr.com/ru/post/988567/


All Articles