继承自:NSObject
**Available in iOS 6.0 and later **
constraint定义了一种位置关系,通过约束来将两个UI对象的位置确定在屏幕上。每个约束遵循如下的这个线性公式:
item1.attribute1 = multiplier × item2.attribute2 + constant
在这个等式中,attribute1和attribute2是两个自动布局的变量,就是通过它们来适配不同的屏幕尺寸。其他的值在你创建约束的时候就该定下来了。举例来说,如果你要定义两个按钮之间的相对位置,你可能会说:“第二个按钮的最左边要距离第一个按钮的最右边有8个point的距离。”这个约束关系的线性公式就如下所示:
// positive values move to the right in left-to-right languages like English.
button2.leading = 1.0 × button1.trailing + 8.0
随后自动布局就会自动调整两个按钮左右边缘的位置,直到上述等式左右相等。这里值得注意的是,自动布局并不是单方面地调整一个按钮来满足条件,而是在需要的时候调整其中的任意一个,甚至两个按钮的位置都调整。
等式相等意味着,你可以改变item的顺序,也就是说你可以对这个等式做随意的一次方程计算。经过改变,等式可以更好地表达两个item之间的约束关系。提醒一句,改变等式别忘了随之改变multiplier(系数)和constant(常数)。举个例子,下面的两个等式就表达相同的约束:
// These equations produce identical constraints
button2.leading = 1.0 × button1.trailing + 8.0
button1.trailing = 1.0 × button2.leading - 8.0
一个有效的布局方案会有一套完整的约束,这意味着由许多等式组成,这些等式有且仅有唯一一个解来定位一个item。要看怎么来定义一个有效布局,请移步 Auto Layout Guide中的Resolving Auto Layout Issues
另外,约束也不只有两者相等这一种,还可以用大于等于(>=),或者小于等于(<=)来描述约束关系。约束还有优先级,范围是1~1000,1000代表必须实现(required),此外都是根据优先级可选择实现(optional)。默认优先级都是1000。
自动布局实现了所有必须实现的约束之后,会根据优先级顺序来尝试去实现可选约束。如果实现不了,就尽可能接近要求,然后继续处理下个可选约束。
通过这套等式、不等式、优先级的组合,你完全可以定义一套极具灵活性的布局,来让你的UI元素自适应任何变化。
创建约束
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary<NSString *,
id> *)metrics
views:(NSDictionary<NSString *,
id> *)views
根据Visual Format Language以及对齐选择项来创建一组对象的约束
+ (instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c
最常用的约束方法,根据两个item的指定attribute来约束item的位置,返回一个NSLayoutConstraint对象。iOS6.0
参数 | 含义 |
---|---|
view1 | 第一个item |
attr1 | item1的参考属性 |
relation | 两个item参考属性之间的关系,详见NSLayoutRelation |
view2 | item1的参照物,item2 |
multiplier | item2的参考属性带入计算时需要缩放的倍数 |
c | item2的属性乘以倍数之后加上这个常量,来得到约束的最终结果 |
论述:也可能出现这样的情况,item1的这个约束并不需要参照物item2(比如说约束宽度),这时候在item2上代入nil,attr2代入NSLayoutAttributeNotAnAttribute。
激活或关闭约束
@property(getter=isActive) BOOL active //iOS8.0
这个属性可以激活或者关闭一个约束,只有激活状态的约束才能影响最终的布局。需要注意的是,如果两个items的不在同一个界面上,则计算结果会报错。新创建的约束对象的状态默认为关闭。
你可以在两个items所在的view上调用addConstraint:
或者removeConstraint:
来激活或者关闭一个约束。但更方便的是直接改变这个属性来替代调用这两个方法。
+ (void)activateConstraints:(NSArray<NSLayoutConstraint *> *)constraints //iOS8.0
+ (void)deactivateConstraints:(NSArray<NSLayoutConstraint *> *)constraints //iOS8.0
这两个方法可以一次性激活/关闭一堆约束的激活状态。通常来讲,用这两个方法比一个个修改约束的active属性在性能上更具优势。
约束内部数据的接口
@property UILayoutPriority priority //(iOS6.0)
这里再提一遍,约束并不是非真即假的,而且如果一个约束并不是必须实现的,那么自动布局也只会去尽可能贴近等式、不等式的计算结果。
一个约束被添加并激活了之后,它的优先级就不能在必须实现和可选实现中来回修改了。但是如果同是可选实现的话,还是能够在约束激活后即时修改优先级的具体数值。
@property(readonly, assign) id firstItem //iOS6.0
@property(readonly) NSLayoutAttribute firstAttribute //iOS6.0
@property(readonly) NSLayoutRelation relation //iOS6.0
@property(readonly, assign) id secondItem //iOS6.0
@property(readonly) NSLayoutAttribute secondAttribute //iOS6.0
@property(readonly) CGFloat multiplier //iOS6.0
上略
@property CGFloat constant //iOS6.0
和其他属性不同,constant在约束创建之后仍可以修改。在现有的约束上修改constant要比移除一个约束,再新建一个除了constant外没啥区别的约束要效率得多。
标识一个约束
@property(copy) NSString *identifier //iOS7.0
约束的标识从description中就能得到
Controlling Constraint Archiving
@property BOOL shouldBeArchived //iOS6.0
When a view is archived, it archives some but not all constraints in its constraints
array. The value ofshouldBeArchived
informs the view if a particular constraint should be archived by the view.
If a constraint is created at runtime in response to the state of the object, it isn't appropriate to archive the constraint. Instead you archive the state that gives rise to the constraint. The default value for this property is NO
枚举常量
enum {
NSLayoutRelationLessThanOrEqual = -1,
NSLayoutRelationEqual = 0,
NSLayoutRelationGreaterThanOrEqual = 1,
};
typedef NSInteger NSLayoutRelation; //iOS6.0
- NSLayoutRelationLessThanOrEqual
前一个属性小于等于后一个属性 - NSLayoutRelationEqual
两个属性完全相等 - NSLayoutRelationGreaterThanOrEqual
前一个属性大于等于后一个属性
typedef enum: NSInteger {
NSLayoutAttributeLeft = 1,
NSLayoutAttributeRight,
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading,
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeBaseline,
NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline,
NSLayoutAttributeFirstBaseline,
NSLayoutAttributeLeftMargin,
NSLayoutAttributeRightMargin,
NSLayoutAttributeTopMargin,
NSLayoutAttributeBottomMargin,
NSLayoutAttributeLeadingMargin,
NSLayoutAttributeTrailingMargin,
NSLayoutAttributeCenterXWithinMargins,
NSLayoutAttributeCenterYWithinMargins,
NSLayoutAttributeNotAnAttribute = 0
} NSLayoutAttribute; //iOS6.0 iOS8.0
- NSLayoutAttributeLeading
在习惯由左向右看的地区,相当于NSLayoutAttributeLeft。在习惯从右至左看的地区,相当于NSLayoutAttributeRight - NSLayoutAttributeTrailing
在习惯由左向右看的地区,相当于NSLayoutAttributeRight。在习惯从右至左看的地区,相当于NSLayoutAttributeLeft -
magin
一系列边界约束属性如下图所示:
- NSLayoutAttributeNotAnAttribute
约束用不着第二个item的时候,第二个属性处选填
enum {
/* choose only one of these */
NSLayoutFormatAlignAllLeft = NSLayoutAttributeLeft,
NSLayoutFormatAlignAllRight = NSLayoutAttributeRight,
NSLayoutFormatAlignAllTop = NSLayoutAttributeTop,
NSLayoutFormatAlignAllBottom = NSLayoutAttributeBottom,
NSLayoutFormatAlignAllLeading = NSLayoutAttributeLeading,
NSLayoutFormatAlignAllTrailing = NSLayoutAttributeTrailing,
NSLayoutFormatAlignAllCenterX = NSLayoutAttributeCenterX,
NSLayoutFormatAlignAllCenterY = NSLayoutAttributeCenterY,
NSLayoutFormatAlignAllBaseline = NSLayoutAttributeBaseline,
NSLayoutFormatAlignmentMask = 0xFF,
/* choose only one of these three */
NSLayoutFormatDirectionLeadingToTrailing = 0 << 8, // default
NSLayoutFormatDirectionLeftToRight = 1 << 8,
NSLayoutFormatDirectionRightToLeft = 2 << 8,
NSLayoutFormatDirectionMask = 0x3 << 8,
};
typedef NSUInteger NSLayoutFormatOptions;
对齐选择项,字面意思不累述
enum {
UILayoutPriorityRequired = 1000,
UILayoutPriorityDefaultHigh = 750,
UILayoutPriorityDefaultLow = 250,
UILayoutPriorityFittingSizeLevel = 50,
};typedef float UILayoutPriority;
iOS7.1后不使用,直接根据需要设定约束的priority属性值即可。