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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19593641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:35:04  来源:igfitidea点击:

Can I change multiplier property for NSLayoutConstraint?

iosiphoneobjective-cnslayoutconstraint

提问by Bimawa

I created two views in one superview, and then added constraints between 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 multiplier property with animation, but I can't figure out how to change the multipler property. (I found _coefficient in private property in header file NSLayoutConstraint.h, but it private.)

现在我想通过动画更改 multiplier 属性,但我不知道如何更改 multiplier 属性。(我在头文件 NSLayoutConstraint.h 的私有属性中发现了 _coefficient,但它是私有的。)

How do I change multipler property?

如何更改multipler属性?

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

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

采纳答案by Ja?ck

If you have only have two sets of multipliers that need to be applied, from iOS8onwards you can add both sets of constraints and decide which should be active at any time:

如果您只有两组需要应用的乘数,从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 ...]

Swift 5.0 version

斯威夫特 5.0 版本

var standardConstraint: NSLayoutConstraint!
var zoomedConstraint: NSLayoutConstraint!

// ...

// switch between constraints
standardConstraint.isActive = false // this line should always be the first line. because you have to deactivate one before activating the other one. or they will conflict.
zoomedConstraint.isActive = true
self.view.layoutIfNeeded() // or using UIView.animate

回答by Andrew Schreiber

Here is an NSLayoutConstraint extension in Swift that makes setting a new multiplier pretty easy:

这是 Swift 中的 NSLayoutConstraint 扩展,它使设置新的乘数变得非常容易:

In Swift 3.0+

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

Demo usage:

演示用法:

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

回答by Fruity Geek

The multiplierproperty is read only. You have to remove the old NSLayoutConstraint and replace it with a new one to modify it.

multiplier属性是只读的。你必须删除旧的 NSLayoutConstraint 并用新的替换它来修改它。

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

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

回答by Evgenii

A helper function I use to change multiplierof an existing layout constraint. It creates and activates a new constraint 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
  }
}

Usage, changing multiplier to 1.2:

用法,将乘数更改为 1.2:

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

回答by Koti Tummala

Objective-C Version for Andrew Schreiber answer

Andrew Schreiber 的 Objective-C 版本答案

Create the category for NSLayoutConstraint Class and add the method in .h file like this

为 NSLayoutConstraint 类创建类别并像这样在 .h 文件中添加方法

    #import <UIKit/UIKit.h>

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

In the .m file

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

Later in the ViewController create the outlet for the constraint you want to update.

稍后在 ViewController 中为要更新的约束创建出口。

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

and update the multiplier where ever you want like below..

并在任何你想要的地方更新乘数,如下所示..

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

回答by Tianfu Dai

You can change the "constant" property instead to achieve the same goal with a little math. Assume your default multiplier on the constraint is 1.0f. This is Xamarin C# code which can be easily translated to objective-c

您可以更改“常量”属性,以通过一点数学实现相同的目标。假设约束的默认乘数是 1.0f。这是 Xamarin C# 代码,可以轻松转换为 Objective-c

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

回答by Robert Dresler

As is in other answers explained: You need to remove constraint and create new one.

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



You can avoid returning new constraint by creating static method for NSLayoutConstraintwith inoutparameter, which allows you to reassign passed constraint

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

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
    }

}

Example usage:

用法示例:

@IBOutlet weak var constraint: NSLayoutConstraint!

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

回答by Rajan Singh

Yes w can change multiplier values just make an extension of NSLayoutConstraint and use it like ->

是的,w 可以更改乘数值,只需扩展 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)

回答by Ullas Kumar

NONE OF THE ABOVE CODE WORKED FOR ME SO AFTER TRYING TO MODIFY MY OWN CODE THIS CODE This code is working in Xcode 10 and swift 4.2

在尝试修改我自己的代码后,上述代码均不适用于我此代码此代码在 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() 
 }

回答by MiguelSlv

Switch by changing the active constraint in code as suggested by many other answers did not work for me. So i created 2 constrains, one installed and the other not, bind both to the code, and then switch by removing one and adding the other.

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

For the sake of completeness, to bind the constrain drag the constrain to the code using mouse right button, just like any other graphic element:

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

enter image description here

在此处输入图片说明

I named one proportionIPad and the other proportionIPhone.

我将一个比例命名为IPad,另一个命名为IPhone。

Then, add the following code, at viewDidLoad

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

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

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

I am using xCode 10 and swift 5.0

我正在使用 xCode 10 和 swift 5.0