Can I change the multiplier property for NSLayoutConstraint?

I created two views in one superview, and then added restrictions between the views:

_indicatorConstrainWidth = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view2 attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f]; [_indicatorConstrainWidth setPriority:UILayoutPriorityDefaultLow]; _indicatorConstrainHeight = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view2 attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0.0f]; [_indicatorConstrainHeight setPriority:UILayoutPriorityDefaultLow]; [self addConstraint:_indicatorConstrainWidth]; [self addConstraint:_indicatorConstrainHeight]; 

Now I want to change the multiplier property using animation, but I cannot figure out how to change the multipler property. (I found _coefficient privately owned in the NSLayoutConstraint.h header file, but it is closed.)

How to change the multipler property?

My workaround is to remove the old restriction and add a new value with a different value for multipler .

+127
ios objective-c iphone nslayoutconstraint
Oct 25 '13 at 15:16
source share
14 answers

If you have only two sets of factors that you need to apply, starting with iOS8 , you can add both sets of restrictions and decide which one should be active at any time:

 NSLayoutConstraint *standardConstraint, *zoomedConstraint; // ... // switch between constraints standardConstraint.active = NO; // this line should always be the first line. because you have to deactivate one before activating the other one. or they will conflict. zoomedConstraint.active = YES; [self.view layoutIfNeeded]; // or using [UIView animate ...] 
+102
Jan 08
source share

Here is the NSLayoutConstraint extension in Swift, which makes setting the new multiplier pretty easy:

 import UIKit extension NSLayoutConstraint { func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint { NSLayoutConstraint.deactivateConstraints([self]) let newConstraint = NSLayoutConstraint( item: firstItem, attribute: firstAttribute, relatedBy: relation, toItem: secondItem, attribute: secondAttribute, multiplier: multiplier, constant: constant) newConstraint.priority = priority newConstraint.shouldBeArchived = shouldBeArchived newConstraint.identifier = identifier NSLayoutConstraint.activateConstraints([newConstraint]) return newConstraint } } 

In Swift 3.0

 import UIKit extension NSLayoutConstraint { /** Change multiplier constraint - parameter multiplier: CGFloat - returns: NSLayoutConstraint */ func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint { NSLayoutConstraint.deactivate([self]) let newConstraint = NSLayoutConstraint( item: firstItem, attribute: firstAttribute, relatedBy: relation, toItem: secondItem, attribute: secondAttribute, multiplier: multiplier, constant: constant) newConstraint.priority = priority newConstraint.shouldBeArchived = self.shouldBeArchived newConstraint.identifier = self.identifier NSLayoutConstraint.activate([newConstraint]) return newConstraint } } 

Using demo:

 @IBOutlet weak var myDemoConstraint:NSLayoutConstraint! override func viewDidLoad() { let newMultiplier:CGFloat = 0.80 myDemoConstraint = myDemoConstraint.setMultiplier(newMultiplier) //If later in view lifecycle, you may need to call view.layoutIfNeeded() } 
+144
Oct 07 '15 at 10:06
source share

The multiplier property is read-only. You must remove the old NSLayoutConstraint and replace it with a new one to change it.

However, since you know that you want to change the factor, you can simply change the constant by multiplying it yourself when changes are needed, which is often less than code.

+52
Oct 25 '13 at 15:27
source share

Helper function used to change the multiplier of an existing layout constraint. It creates and activates a new restriction and deactivates the old one.

 struct MyConstraint { static func changeMultiplier(_ constraint: NSLayoutConstraint, multiplier: CGFloat) -> NSLayoutConstraint { let newConstraint = NSLayoutConstraint( item: constraint.firstItem, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: constraint.secondItem, attribute: constraint.secondAttribute, multiplier: multiplier, constant: constraint.constant) newConstraint.priority = constraint.priority NSLayoutConstraint.deactivate([constraint]) NSLayoutConstraint.activate([newConstraint]) return newConstraint } } 

Use, change the multiplier to 1.2:

 constraint = MyConstraint.changeMultiplier(constraint, multiplier: 1.2) 
+45
Sep 30 '15 at 6:57
source share

Objective-C Version for Andrew Schreiber Answer

Create a category for the NSLayoutConstraint class and add the method to the .h file, like this

  #import <UIKit/UIKit.h> @interface NSLayoutConstraint (Multiplier) -(instancetype)updateMultiplier:(CGFloat)multiplier; @end 

In the .m file

 #import "NSLayoutConstraint+Multiplier.h" @implementation NSLayoutConstraint (Multiplier) -(instancetype)updateMultiplier:(CGFloat)multiplier { [NSLayoutConstraint deactivateConstraints:[NSArray arrayWithObjects:self, nil]]; NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:self.firstItem attribute:self.firstAttribute relatedBy:self.relation toItem:self.secondItem attribute:self.secondAttribute multiplier:multiplier constant:self.constant]; [newConstraint setPriority:self.priority]; newConstraint.shouldBeArchived = self.shouldBeArchived; newConstraint.identifier = self.identifier; newConstraint.active = true; [NSLayoutConstraint activateConstraints:[NSArray arrayWithObjects:newConstraint, nil]]; //NSLayoutConstraint.activateConstraints([newConstraint]) return newConstraint; } @end 

Later in the ViewController, create the output for the constraint that you want to update.

 @property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint; 

and update the multiplier whenever you want, as shown below.

 self.topConstraint = [self.topConstraint updateMultiplier:0.9099]; 
+39
May 16 '16 at 10:44
source share

Instead, you can change the constant property to achieve the same goal with a little math. Suppose your default multiplier for the constraint is 1.0f. This is Xamarin C # code that can be easily translated to target-c

 private void SetMultiplier(nfloat multiplier) { FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier); } 
+11
Feb 21 '17 at 20:30
source share

As explained in other answers: You need to remove the restriction and create a new one.




You can avoid returning a new constraint by creating a static method for NSLayoutConstraint with an inout that allows you to reassign the passed constraint

 import UIKit extension NSLayoutConstraint { static func setMultiplier(_ multiplier: CGFloat, of constraint: inout NSLayoutConstraint) { NSLayoutConstraint.deactivate([constraint]) let newConstraint = NSLayoutConstraint(item: constraint.firstItem, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: constraint.secondItem, attribute: constraint.secondAttribute, multiplier: multiplier, constant: constraint.constant) newConstraint.priority = constraint.priority newConstraint.shouldBeArchived = constraint.shouldBeArchived newConstraint.identifier = constraint.identifier NSLayoutConstraint.activate([newConstraint]) constraint = newConstraint } } 

Usage example:

 @IBOutlet weak var constraint: NSLayoutConstraint! override func viewDidLoad() { NSLayoutConstraint.setMultiplier(0.8, of: &constraint) // view.layoutIfNeeded() } 
+2
Mar 20 '19 at 13:08
source share

To change the value of the multiplier, follow these steps: 1. Create outputs for the required restriction, for example, someConstraint. 2. Change the value of the multiplier, for example someConstraint.constant * = 0.8 or any value of the multiplier.

That's all, happy coding.

+1
Sep 17 '18 at 8:19
source share

Yes, we can change the multiplier values, just make the extension NSLayoutConstraint and use it as ->

  func setMultiplier(_ multiplier:CGFloat) -> NSLayoutConstraint { NSLayoutConstraint.deactivate([self]) let newConstraint = NSLayoutConstraint( item: firstItem!, attribute: firstAttribute, relatedBy: relation, toItem: secondItem, attribute: secondAttribute, multiplier: multiplier, constant: constant) newConstraint.priority = priority newConstraint.shouldBeArchived = shouldBeArchived newConstraint.identifier = identifier NSLayoutConstraint.activate([newConstraint]) return newConstraint } self.mainImageViewHeightMultiplier = self.mainImageViewHeightMultiplier.setMultiplier(375.0/812.0) 
+1
Jun 13 '19 at 6:52
source share

Here is an answer based on @Tianfu's answer in C #. Other answers requiring activation and deactivation of restrictions did not help me.

  var isMapZoomed = false @IBAction func didTapMapZoom(_ sender: UIButton) { let offset = -1.0*graphHeightConstraint.secondItem!.frame.height*(1.0 - graphHeightConstraint.multiplier) graphHeightConstraint.constant = (isMapZoomed) ? offset : 0.0 isMapZoomed = !isMapZoomed self.view.layoutIfNeeded() } 
0
Jan 21 '18 at 20:51
source share

suppose view1 in a storyboard has a width limit to the main view (self.view in code) like this

  view1Width = view * 0.7 + constant 

instead of creating 2 constraints and switching between them by changing the active ORRR property [by deactivating the current constraint with the old multiplier and creating a new one with the new multiplier] leave its 1 constraint and play with it constant

Suppose I want to change view1 multiplier to 0.9 instead of 0.7

I can do it

  self.view1Width.constant += self.view.frame.size.width * 0.2 

you do the same for subtraction

0
Jan 25 '18 at 22:43
source share

None of the above code worked for me, so after trying to change my own code this code This code works in Xcode 10 and swift 4.2

 import UIKit extension NSLayoutConstraint { /** Change multiplier constraint - parameter multiplier: CGFloat - returns: NSLayoutConstraintfor */i func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint { NSLayoutConstraint.deactivate([self]) let newConstraint = NSLayoutConstraint( item: firstItem, attribute: firstAttribute, relatedBy: relation, toItem: secondItem, attribute: secondAttribute, multiplier: multiplier, constant: constant) newConstraint.priority = priority newConstraint.shouldBeArchived = self.shouldBeArchived newConstraint.identifier = self.identifier NSLayoutConstraint.activate([newConstraint]) return newConstraint } } @IBOutlet weak var myDemoConstraint:NSLayoutConstraint! override func viewDidLoad() { let newMultiplier:CGFloat = 0.80 myDemoConstraint = myDemoConstraint.setMultiplier(newMultiplier) //If later in view lifecycle, you may need to call view.layoutIfNeeded() } 
0
Mar 17 '19 at 8:23
source share

Switching by changing the active restriction in the code, as suggested in many other answers, did not work for me. So, I created 2 restrictions, one is set, and the other is not, connect both codes, and then switch by deleting one and adding the other.

To complete the binding of the constraint, drag the constraint into the code using the right mouse button, like any other graphic element:

enter image description here

I called one proportion of the iPad and another proportion of the iPhone.

Then add the following code to viewDidLoad

 override open func viewDidLoad() { super.viewDidLoad() if ... { view.removeConstraint(proportionIphone) view.addConstraint(proportionIpad) } } 

I am using xCode 10 and Swift 5.0

0
May 18 '19 at 19:30
source share

You can read:

 var multiplier: CGFloat The multiplier applied to the second attribute participating in the constraint. 

on this page . Does this not mean that you need to be able to modify the multiplier (since this is var)?

-one
Oct 11 '17 at 7:26
source share



All Articles