繼承自:NSObject
**Available in iOS 6.0 and later **
constraint定義了一種位置關(guān)系,通過(guò)約束來(lái)將兩個(gè)UI對(duì)象的位置確定在屏幕上。每個(gè)約束遵循如下的這個(gè)線(xiàn)性公式:
item1.attribute1 = multiplier × item2.attribute2 + constant
在這個(gè)等式中,attribute1和attribute2是兩個(gè)自動(dòng)布局的變量,就是通過(guò)它們來(lái)適配不同的屏幕尺寸。其他的值在你創(chuàng)建約束的時(shí)候就該定下來(lái)了。舉例來(lái)說(shuō),如果你要定義兩個(gè)按鈕之間的相對(duì)位置,你可能會(huì)說(shuō):“第二個(gè)按鈕的最左邊要距離第一個(gè)按鈕的最右邊有8個(gè)point的距離?!边@個(gè)約束關(guān)系的線(xiàn)性公式就如下所示:
// positive values move to the right in left-to-right languages like English.
button2.leading = 1.0 × button1.trailing + 8.0
隨后自動(dòng)布局就會(huì)自動(dòng)調(diào)整兩個(gè)按鈕左右邊緣的位置,直到上述等式左右相等。這里值得注意的是,自動(dòng)布局并不是單方面地調(diào)整一個(gè)按鈕來(lái)滿(mǎn)足條件,而是在需要的時(shí)候調(diào)整其中的任意一個(gè),甚至兩個(gè)按鈕的位置都調(diào)整。
等式相等意味著,你可以改變item的順序,也就是說(shuō)你可以對(duì)這個(gè)等式做隨意的一次方程計(jì)算。經(jīng)過(guò)改變,等式可以更好地表達(dá)兩個(gè)item之間的約束關(guān)系。提醒一句,改變等式別忘了隨之改變multiplier(系數(shù))和constant(常數(shù))。舉個(gè)例子,下面的兩個(gè)等式就表達(dá)相同的約束:
// These equations produce identical constraints
button2.leading = 1.0 × button1.trailing + 8.0
button1.trailing = 1.0 × button2.leading - 8.0
一個(gè)有效的布局方案會(huì)有一套完整的約束,這意味著由許多等式組成,這些等式有且僅有唯一一個(gè)解來(lái)定位一個(gè)item。要看怎么來(lái)定義一個(gè)有效布局,請(qǐng)移步 Auto Layout Guide中的Resolving Auto Layout Issues
另外,約束也不只有兩者相等這一種,還可以用大于等于(>=),或者小于等于(<=)來(lái)描述約束關(guān)系。約束還有優(yōu)先級(jí),范圍是1~1000,1000代表必須實(shí)現(xiàn)(required),此外都是根據(jù)優(yōu)先級(jí)可選擇實(shí)現(xiàn)(optional)。默認(rèn)優(yōu)先級(jí)都是1000。
自動(dòng)布局實(shí)現(xiàn)了所有必須實(shí)現(xiàn)的約束之后,會(huì)根據(jù)優(yōu)先級(jí)順序來(lái)嘗試去實(shí)現(xiàn)可選約束。如果實(shí)現(xiàn)不了,就盡可能接近要求,然后繼續(xù)處理下個(gè)可選約束。
通過(guò)這套等式、不等式、優(yōu)先級(jí)的組合,你完全可以定義一套極具靈活性的布局,來(lái)讓你的UI元素自適應(yīng)任何變化。
創(chuàng)建約束
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary<NSString *,
id> *)metrics
views:(NSDictionary<NSString *,
id> *)views
根據(jù)Visual Format Language以及對(duì)齊選擇項(xiàng)來(lái)創(chuàng)建一組對(duì)象的約束
+ (instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c
最常用的約束方法,根據(jù)兩個(gè)item的指定attribute來(lái)約束item的位置,返回一個(gè)NSLayoutConstraint對(duì)象。iOS6.0
| 參數(shù) | 含義 |
|---|---|
| view1 | 第一個(gè)item |
| attr1 | item1的參考屬性 |
| relation | 兩個(gè)item參考屬性之間的關(guān)系,詳見(jiàn)NSLayoutRelation |
| view2 | item1的參照物,item2 |
| multiplier | item2的參考屬性帶入計(jì)算時(shí)需要縮放的倍數(shù) |
| c | item2的屬性乘以倍數(shù)之后加上這個(gè)常量,來(lái)得到約束的最終結(jié)果 |
論述:也可能出現(xiàn)這樣的情況,item1的這個(gè)約束并不需要參照物item2(比如說(shuō)約束寬度),這時(shí)候在item2上代入nil,attr2代入NSLayoutAttributeNotAnAttribute。
激活或關(guān)閉約束
@property(getter=isActive) BOOL active //iOS8.0
這個(gè)屬性可以激活或者關(guān)閉一個(gè)約束,只有激活狀態(tài)的約束才能影響最終的布局。需要注意的是,如果兩個(gè)items的不在同一個(gè)界面上,則計(jì)算結(jié)果會(huì)報(bào)錯(cuò)。新創(chuàng)建的約束對(duì)象的狀態(tài)默認(rèn)為關(guān)閉。
你可以在兩個(gè)items所在的view上調(diào)用addConstraint:或者removeConstraint:來(lái)激活或者關(guān)閉一個(gè)約束。但更方便的是直接改變這個(gè)屬性來(lái)替代調(diào)用這兩個(gè)方法。
+ (void)activateConstraints:(NSArray<NSLayoutConstraint *> *)constraints //iOS8.0
+ (void)deactivateConstraints:(NSArray<NSLayoutConstraint *> *)constraints //iOS8.0
這兩個(gè)方法可以一次性激活/關(guān)閉一堆約束的激活狀態(tài)。通常來(lái)講,用這兩個(gè)方法比一個(gè)個(gè)修改約束的active屬性在性能上更具優(yōu)勢(shì)。
約束內(nèi)部數(shù)據(jù)的接口
@property UILayoutPriority priority //(iOS6.0)
這里再提一遍,約束并不是非真即假的,而且如果一個(gè)約束并不是必須實(shí)現(xiàn)的,那么自動(dòng)布局也只會(huì)去盡可能貼近等式、不等式的計(jì)算結(jié)果。
一個(gè)約束被添加并激活了之后,它的優(yōu)先級(jí)就不能在必須實(shí)現(xiàn)和可選實(shí)現(xiàn)中來(lái)回修改了。但是如果同是可選實(shí)現(xiàn)的話(huà),還是能夠在約束激活后即時(shí)修改優(yōu)先級(jí)的具體數(shù)值。
@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在約束創(chuàng)建之后仍可以修改。在現(xiàn)有的約束上修改constant要比移除一個(gè)約束,再新建一個(gè)除了constant外沒(méi)啥區(qū)別的約束要效率得多。
標(biāo)識(shí)一個(gè)約束
@property(copy) NSString *identifier //iOS7.0
約束的標(biāo)識(shí)從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
前一個(gè)屬性小于等于后一個(gè)屬性 - NSLayoutRelationEqual
兩個(gè)屬性完全相等 - NSLayoutRelationGreaterThanOrEqual
前一個(gè)屬性大于等于后一個(gè)屬性
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
在習(xí)慣由左向右看的地區(qū),相當(dāng)于NSLayoutAttributeLeft。在習(xí)慣從右至左看的地區(qū),相當(dāng)于NSLayoutAttributeRight - NSLayoutAttributeTrailing
在習(xí)慣由左向右看的地區(qū),相當(dāng)于NSLayoutAttributeRight。在習(xí)慣從右至左看的地區(qū),相當(dāng)于NSLayoutAttributeLeft -
magin
一系列邊界約束屬性如下圖所示:
36R3qqu.png - NSLayoutAttributeNotAnAttribute
約束用不著第二個(gè)item的時(shí)候,第二個(gè)屬性處選填
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;
對(duì)齊選擇項(xiàng),字面意思不累述
enum {
UILayoutPriorityRequired = 1000,
UILayoutPriorityDefaultHigh = 750,
UILayoutPriorityDefaultLow = 250,
UILayoutPriorityFittingSizeLevel = 50,
};typedef float UILayoutPriority;
iOS7.1后不使用,直接根據(jù)需要設(shè)定約束的priority屬性值即可。
