Ios 我可以更改NSLayoutConstraint的乘数属性吗?
我在一个superview中创建了两个视图,然后在视图之间添加了约束:Ios 我可以更改NSLayoutConstraint的乘数属性吗?,ios,iphone,objective-c,nslayoutconstraint,Ios,Iphone,Objective C,Nslayoutconstraint,我在一个superview中创建了两个视图,然后在视图之间添加了约束: _indicatorConstrainWidth = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view2 attribute:NSLayoutAttributeWidth multiplier:1.0f const
_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的私有属性中找到了_系数,但它是私有的。)
如何更改乘法器属性
我的解决方法是删除旧约束,并为
乘数
添加具有不同值的新约束乘数
属性为只读。您必须删除旧的NSLayoutConstraint,并将其替换为新的约束才能对其进行修改
但是,由于您知道要更改乘法器,因此可以在需要更改时通过自己将其相乘来更改常量,这通常不需要太多代码 如果您只有两组需要应用的乘数,则从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版
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
用于更改现有布局约束的乘数的辅助函数。它创建并激活一个新约束,并停用旧约束
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)
以下是Swift中的NSLayoutConstraint扩展,它使设置新乘数变得非常简单: 使用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()
}
Andrew Schreiber答案的Objective-C版本 为NSLayoutConstraint类创建类别,并在.h文件中添加如下方法
#import <UIKit/UIKit.h>
@interface NSLayoutConstraint (Multiplier)
-(instancetype)updateMultiplier:(CGFloat)multiplier;
@end
稍后在ViewController中,为要更新的约束创建出口
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
并随时更新乘数,如下图所示
self.topConstraint = [self.topConstraint updateMultiplier:0.9099];
您可以更改“常量”属性,而不是通过一点数学运算来实现相同的目标。假设约束的默认乘数为1.0f。这是Xamarin C#代码,可以很容易地翻译成objective-C
private void SetMultiplier(nfloat multiplier)
{
FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier);
}
人们可以读到:
var multiplier: CGFloat
The multiplier applied to the second attribute participating in the constraint.
在这个问题上。这难道不意味着我们应该能够修改乘数(因为它是一个var)?这里有一个基于@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()
}
要更改乘数的值,请执行以下步骤: 1.为所需约束(例如someConstraint)创建插座。 2.更改乘数值,如someConstraint.constant*=0.8或任何乘数值
就是这样,快乐编码。以上代码都不适合我 所以在尝试修改我自己的代码之后 此代码在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()
}
正如其他答案所解释的:您需要删除约束并创建新的约束
您可以通过使用
inout
参数为NSLayoutConstraint
创建静态方法来避免返回新约束,该方法允许您重新分配传递的约束
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()
}
按照许多其他答案的建议,通过更改代码中的活动约束进行切换对我不起作用。因此,我创建了两个约束,一个已安装,另一个未安装,将两者绑定到代码,然后通过删除一个并添加另一个进行切换 为了完整起见,要绑定约束,请使用鼠标右键将约束拖动到代码中,就像任何其他图形元素一样: 我命名了一个比例键盘和另一个比例电话 然后,在viewDidLoad中添加以下代码
override open func viewDidLoad() {
super.viewDidLoad()
if ... {
view.removeConstraint(proportionIphone)
view.addConstraint(proportionIpad)
}
}
我正在使用xCode 10和swift 5.0是的,我们可以更改乘数值,只是扩展了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)
在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
}
}
我有办法。无需重新创建约束 假设您有一个要约束其纵横比以匹配图像纵横比的imageView
@IBOutlet弱var viewHeightConstraint:NSLayoutConstraint 让heightOfSuperview=self.view.bounds.height viewHeightConstraint.constant=监视高度*0.5
//这与乘数的效果相同您当前删除旧约束并添加新约束的方法是正确的选择。我知道这感觉不“对”,但这是你应该做的。我认为乘数不应该是常数。这是一个糟糕的设计。@Borzh为什么我通过乘法器进行自适应约束。是的,我还想改变乘数,这样视图可以保持它与父视图的比率(不是宽度/高度,而是宽度或高度),但奇怪的是苹果不允许这样做。我的意思是,这是苹果公司的糟糕设计,不是你的。这是一个伟大的演讲,涵盖了这一点,而且乘数是只读的,这似乎更奇怪。我没想到@具有讽刺意味的是,“常量”值可以改变,而乘数却不能。这是错误的。NSLayoutConstraint指定仿射(in)相等约束。例如:“View1.bottomY=A*View2.bottomY+B”'A'是乘数,'B'是常数。无论您如何调整B,它都不会产生与更改A的值相同的方程式。@FruityGeek好的,因此您使用
View2.bottomY
的当前值来计算约束的新常量。这是有缺陷的,因为View2.bottomY
的旧值将在常量中烘焙,因此您必须放弃声明式样式,并随时手动更新约束View2.bottomY
更改。在这种情况下,您也可以手动布局。如果我希望NSLayoutConstraint在没有额外代码的情况下响应边界更改或设备旋转,这将不起作用。@MICNGUEN是的,您可以:)如果您需要两组以上的约束,您可以添加任意数量的约束并更改它们的设置
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
}
}
let multiplier = image.size.width / image.size.height
let (w, h) = (imageView.bounds.width, imageView.bounds.height)
let expectedW = h * multiplier
let diff = expectedW - h
imageViewAspectConstraint.constant = image.size.width >= image.size.height ? diff : -diff // multiplier is read-only, but constant is RW