我可以更改NSLayoutConstraint的乘数属性吗?

时间:2013-10-25 15:16:10

标签: ios iphone objective-c nslayoutconstraint

我在一个superview中创建了两个视图,然后在视图之间添加了约束:

_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];

现在我想用动画更改乘数属性,但我无法弄清楚如何更改多重属性。 (我在头文件NSLayoutConstraint.h中的私有属性中找到_coefficient,但它是私有的。)

如何更改多重属性?

我的解决方法是删除旧约束,并为multipler添加不同值的新约束。

16 个答案:

答案 0 :(得分:144)

这是Swift中的NSLayoutConstraint扩展,它可以很容易地设置一个新的乘数:

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
    }
}

在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
    }
}

演示用法:

@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() 
}

答案 1 :(得分:101)

如果您只需要应用两组乘数,从 iOS8 开始,您可以添加两组约束并决定哪些约束应该是活动的:

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 ...]

答案 2 :(得分:51)

multiplier属性是只读的。您必须删除旧的NSLayoutConstraint并将其替换为新的NSLayoutConstraint以进行修改。

但是,由于您知道要更改乘数,因此您可以通过在需要更改(通常更少的代码)时自己乘以常量来更改常量。

答案 3 :(得分:45)

我用来更改现有布局约束的乘数的辅助函数。它创建并激活新约束并停用旧约束。

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
  }
}

用法,将乘数改为1.2:

constraint = MyConstraint.changeMultiplier(constraint, multiplier: 1.2)

答案 4 :(得分:39)

Andrew Schreiber答案的Objective-C版本

为NSLayoutConstraint类创建类别,并将此方法添加到.h文件中,如此

    #import <UIKit/UIKit.h>

@interface NSLayoutConstraint (Multiplier)
-(instancetype)updateMultiplier:(CGFloat)multiplier;
@end

在.m文件中

#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

稍后在ViewController中为您要更新的约束创建插座。

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

并在下面的任何地方更新乘数......

self.topConstraint = [self.topConstraint updateMultiplier:0.9099];

答案 5 :(得分:11)

您可以更改“常量”属性,而不是通过一点数学来实现相同的目标。假设约束的默认乘数是1.0f。这是Xamarin C#代码,可以很容易地翻译成objective-c

private void SetMultiplier(nfloat multiplier)
{
    FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier);
}

答案 6 :(得分:2)

正如在其他答案中所解释的:您需要删除约束并创建新约束。


您可以通过使用NSLayoutConstraint参数为inout创建静态方法来避免返回新约束,该方法允许您重新分配传递的约束

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
    }

}

用法示例:

@IBOutlet weak var constraint: NSLayoutConstraint!

override func viewDidLoad() {
    NSLayoutConstraint.setMultiplier(0.8, of: &constraint)
    // view.layoutIfNeeded() 
}

答案 7 :(得分:1)

要更改乘数的值,请按照以下步骤操作:  1.为所需的约束(例如someConstraint)创建出口。  2.更改乘数值,例如 someConstraint.constant * = 0.8 或任何乘数。

就这样,祝您编程愉快。

答案 8 :(得分:1)

是的,只要更改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 = shouldBeArchived
        newConstraint.identifier = identifier

        NSLayoutConstraint.activate([newConstraint])
        return newConstraint
    }

            self.mainImageViewHeightMultiplier = self.mainImageViewHeightMultiplier.setMultiplier(375.0/812.0)

答案 9 :(得分:1)

在Swift 5.x中,您可以使用:

extension NSLayoutConstraint {
    func setMultiplier(multiplier: CGFloat) -> NSLayoutConstraint {
        guard let firstItem = firstItem else {
            return self
        }
        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
    }
}

答案 10 :(得分:0)

这是基于@Tianfu在C#中的答案的答案。其他需要激活和停用约束的答案对我来说不起作用。

    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()
}

答案 11 :(得分:0)

以上代码均不适用于我 因此,尝试修改自己的代码后,此代码    该代码可在Xcode 10和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() 
 }

答案 12 :(得分:0)

如许多其他答案所建议的那样,通过更改代码中的活动约束进行切换对我不起作用。因此,我创建了2个约束,一个已安装,另一个未绑定,将两者都绑定到代码,然后通过删除一个并添加另一个来进行切换。

为了完整起见,要绑定约束,请使用鼠标右键将约束拖至代码,就像其他任何图形元素一样:

enter image description here

我命名了一个比例IPad,另一个比例命名为iPhone。

然后,在viewDidLoad处添加以下代码

override open func viewDidLoad() {
   super.viewDidLoad()
   if ... {

       view.removeConstraint(proportionIphone)
       view.addConstraint(proportionIpad) 
   }     
}

我正在使用xCode 10和Swift 5.0

答案 13 :(得分:0)

我有办法。无需重新创建约束。

假设您有一个想要限制其纵横比以匹配图像纵横比的 imageView。

  1. 为 imageView 创建纵横比约束,并将乘数设置为 1,将常量设置为 0。
  2. 为纵横比约束创建一个出口。
  3. 在运行时更改约束常量值,根据您加载的图像:
import excel2json
excel2json.convert_from_file('test.xls')

答案 14 :(得分:0)

@IBOutlet 弱变量 viewHeightConstraint: NSLayoutConstraint!

让 heightOfSuperview = self.view.bounds.height

viewHeightConstraint.constant = heightOfSuperview * 0.5

// 与乘法器效果相同

答案 15 :(得分:-1)

人们可以读到:

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

就此documentation page。这是否意味着一个人应该能够修改乘数(因为它是一个var)?