Objective-C的Auto Layout(自動(dòng)布局)學(xué)習(xí)筆記

Auto Layout

Auto Layout 即自動(dòng)布局,在iOS6引入,不同于frame框架式的布局,自動(dòng)布局根據(jù)視圖間的相對(duì)約束來確定視圖位置與大小,使視圖得以動(dòng)態(tài)的適應(yīng)位置與大小的變化,匹配不同尺寸的設(shè)備,從而節(jié)省大量設(shè)置或更新視圖位置與大小的代碼。自動(dòng)布局涉及:NSLayoutConstraint(布局約束)、NSLayoutAnchor(布局錨)、UILayoutGuide(布局占位)、SizeClasses(屏幕適配)、Constraints in Interface Builder(故事板約束)等。

\color{red}{例如一個(gè)簡單的frame布局:}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 200)];
    view.backgroundColor = [UIColor greenColor];
    [self.view addSubview:view];
}

正常情況下,顯示沒有問題:

截屏2022-04-11 下午3.29.59.png

但當(dāng)設(shè)備發(fā)生旋轉(zhuǎn)時(shí),問題便產(chǎn)生了:

截屏2022-04-11 下午3.31.08.png

這顯然不是我們想看到的效果,如果使用約束進(jìn)行布局:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIView *view2 = [[UIView alloc]initWithFrame:CGRectZero];
    view2.backgroundColor = [UIColor greenColor];
    view2.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:view2];

    NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:view2
                                                                     attribute:NSLayoutAttributeTop
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:self.view
                                                                     attribute:NSLayoutAttributeTop
                                                                    multiplier:1.0
                                                                      constant:0];

    NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:view2
                                                                     attribute:NSLayoutAttributeLeft
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:self.view
                                                                     attribute:NSLayoutAttributeLeft
                                                                    multiplier:1.0
                                                                      constant:0];

    NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:view2
                                                                     attribute:NSLayoutAttributeWidth
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:self.view
                                                                     attribute:NSLayoutAttributeWidth
                                                                    multiplier:1.0
                                                                      constant:0];

    NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:view2
                                                                     attribute:NSLayoutAttributeHeight
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:nil
                                                                     attribute:NSLayoutAttributeNotAnAttribute
                                                                    multiplier:1.0
                                                                      constant:200];
    [self.view addConstraint:topConstraint];
    [self.view addConstraint:leftConstraint];
    [self.view addConstraint:widthConstraint];
    [self.view addConstraint:heightConstraint];
}

正常情況下,顯示沒有問題:

截屏2022-04-11 下午3.36.47.png

當(dāng)設(shè)備發(fā)生旋轉(zhuǎn)時(shí),顯示也是沒有問題:

截屏2022-04-11 下午3.40.53.png

當(dāng)然現(xiàn)在添加約束的代碼仍舊非常繁瑣,寫起來仍舊讓人想在心里默默的數(shù)著羊駝,但是該看的api還是要看,誰讓我們要去學(xué)習(xí),要去認(rèn)知呢。

UIView (UIConstraintBasedCompatibility) - 基于約束的兼容性

@property(nonatomic) BOOL translatesAutoresizingMaskIntoConstraints API_AVAILABLE(ios(6.0));

屬性描述一個(gè)布爾值,如果此屬性的值為YES,系統(tǒng)將基于視圖的大小和位置的掩碼為視圖轉(zhuǎn)換為自動(dòng)布局約束,而約束完全指定了視圖的大小和位置,因此,在不引入沖突的情況下,無法添加其他約束來修改此大小或位置。如果要使用“Auto Layout”動(dòng)態(tài)計(jì)算視圖的大小和位置,則必須將此屬性設(shè)置為NO,然后為視圖提供一組無歧義、無沖突的約束。默認(rèn)情況下,對(duì)于以編程方式創(chuàng)建的任何視圖,該屬性都設(shè)置為YES。如果在Interface Builder中添加視圖,系統(tǒng)會(huì)自動(dòng)將此屬性設(shè)置為NO。這個(gè)擴(kuò)展于UIView中的屬性也就是我們使用自動(dòng)布局設(shè)置約束的先決條件了。

NSLayoutConstraint - 布局約束

NSLayoutConstraint,描述一條約束的對(duì)象,基于約束的布局系統(tǒng)必須滿足的兩個(gè)用戶界面對(duì)象之間的關(guān)系。每個(gè)約束都是一個(gè)線性方程,格式如下:

item1.attribute1 = multiplier × item2.attribute2 + constant

在這個(gè)等式中,attribute1和attribute2是Auto Layout在解決這些約束時(shí)可以調(diào)整的變量。其他值在創(chuàng)建約束時(shí)定義。例如,如果定義兩個(gè)按鈕的相對(duì)位置,可能這樣描述 “第二個(gè)按鈕的前緣應(yīng)該在第一個(gè)按鈕的后緣之后8個(gè)點(diǎn)。” 此關(guān)系的線性方程如下所示(在英語等從左到右的語言中,正值向右移動(dòng)):

button2.leading = 1.0 × button1.trailing + 8.0

Auto Layout然后修改指定的前邊和后緣的值,直到公式的兩邊相等。注意,Auto Layout不會(huì)簡單地將該等式右側(cè)的值指定給左側(cè)。相反,系統(tǒng)可以根據(jù)需要修改其中一個(gè)屬性或兩個(gè)屬性來解決此約束。

約束是表達(dá)式(而不是賦值運(yùn)算符)這一事實(shí)意味著可以根據(jù)需要切換方程中項(xiàng)目的順序,以更清楚地表達(dá)所需的關(guān)系。但是,如果切換順序,還必須反轉(zhuǎn)乘數(shù)和常數(shù)。例如,以下兩個(gè)等式產(chǎn)生相同的約束:

button2.leading = 1.0 × button1.trailing + 8.0

button1.trailing = 1.0 × button2.leading - 8.0

一個(gè)有效的布局被定義為一組約束,并且只有一個(gè)可能的解決方案。有效的布局也被稱為無歧義、無沖突的布局。具有多個(gè)解決方案的約束是不明確的。沒有有效解決方案的約束是沖突的

此外,約束并不局限于等式關(guān)系。它們還可以使用大于或等于(>=)或小于或等于(<=)來描述這兩個(gè)屬性之間的關(guān)系。約束的priority(優(yōu)先級(jí))也在1到1000之間,priority為1000的約束是必要約束,所有低于1000的priority都是可選約束,默認(rèn)情況下,所有約束都是必需的(priority = 1000)

在求解完所需的約束條件后,Auto Layout將嘗試按照從高到低的優(yōu)先級(jí)順序求解所有可選約束條件。如果它不能解決一個(gè)可選的約束,它就會(huì)嘗試盡可能接近期望的結(jié)果,然后轉(zhuǎn)向下一個(gè)約束。這種不相等、相等和優(yōu)先級(jí)的組合提供了很大的靈活性和權(quán)力。通過組合多個(gè)約束,可以定義隨著用戶界面中元素的大小和位置變化而動(dòng)態(tài)適應(yīng)的布局。

NSLayoutConstraint的常用屬性
@property CGFloat constant;

屬性描述加入到參與約束的第二個(gè)屬性的乘數(shù)上的常數(shù)。即item1.attribute1 = multiplier × item2.attribute2 + constant中的constant。與其他屬性不同,該常量可以在約束創(chuàng)建后修改。 在現(xiàn)有約束上設(shè)置常量比刪除約束并添加一個(gè)與舊約束完全相同的新約束要好得多

@property UILayoutPriority priority;

屬性描述約束的優(yōu)先級(jí)

  • 系統(tǒng)提供的約束優(yōu)先級(jí):
//必要的約束。不要指定超過此數(shù)字的布局約束優(yōu)先級(jí)。(最大優(yōu)先級(jí))
static const UILayoutPriority UILayoutPriorityRequired API_AVAILABLE(ios(6.0)) = 1000; 
//按鈕阻止壓縮其內(nèi)容的優(yōu)先級(jí)。(可作為中位優(yōu)先級(jí))
static const UILayoutPriority UILayoutPriorityDefaultHigh API_AVAILABLE(ios(6.0)) = 750; 
//這可能是拖動(dòng)調(diào)整窗口最終場(chǎng)景大小的適當(dāng)優(yōu)先級(jí)。
static const UILayoutPriority UILayoutPriorityDragThatCanResizeScene API_AVAILABLE(macCatalyst(13.0)) = 510;
//這是窗口的場(chǎng)景希望保持相同大小的優(yōu)先級(jí)。一般來說,在這種優(yōu)先級(jí)下進(jìn)行約束是不合適的。
static const UILayoutPriority UILayoutPrioritySceneSizeStayPut API_AVAILABLE(macCatalyst(13.0)) = 500; 
//這是分屏視圖分隔符被拖動(dòng)時(shí)的優(yōu)先級(jí)。它不會(huì)調(diào)整窗口場(chǎng)景的大小。
static const UILayoutPriority UILayoutPriorityDragThatCannotResizeScene API_AVAILABLE(macCatalyst(13.0)) = 490; 
//這是按鈕水平擁抱其內(nèi)容的優(yōu)先級(jí)。(可作為低位優(yōu)先級(jí))
static const UILayoutPriority UILayoutPriorityDefaultLow API_AVAILABLE(ios(6.0)) = 250; 
//發(fā)送-[UIView systemLayoutSizeFittingSize:]時(shí),將計(jì)算與目標(biāo)大小(參數(shù))最接近的大小。
//UILayoutPriorityFittingSizeLevel是視圖希望符合該計(jì)算中目標(biāo)大小的優(yōu)先級(jí)。
//很低。一般來說,在這個(gè)優(yōu)先級(jí)上進(jìn)行約束是不合適的。
static const UILayoutPriority UILayoutPriorityFittingSizeLevel API_AVAILABLE(ios(6.0)) = 50;
@property (getter=isActive) BOOL active API_AVAILABLE(macos(10.10), ios(8.0));

屬性描述約束的活動(dòng)狀態(tài)。可以通過更改此屬性來激活或停用約束。只有活動(dòng)約束會(huì)影響計(jì)算的布局。對(duì)于新創(chuàng)建的約束,默認(rèn)情況下active屬性是NO。激活或停用約束會(huì)在視圖上調(diào)用addConstraint:和removeconconstraint:,在為ios8.0或更高版本開發(fā)時(shí),應(yīng)該使用這個(gè)屬性,而不是直接調(diào)用addConstraint:或removeconconstraint:。

@property (nullable, readonly, assign) id firstItem;

屬性描述 :要添加約束的對(duì)象。

@property (nullable, readonly, assign) id secondItem;

屬性描述 :要添加約束對(duì)象的參照對(duì)象。

@property (readonly) NSLayoutAttribute firstAttribute;

屬性描述 :添加約束的對(duì)象的約束屬性。

@property (readonly) NSLayoutAttribute secondAttribute;

屬性描述 :添加約束時(shí),作為約束對(duì)象的參照對(duì)象的約束屬性。

NSLayoutConstraint的常用函數(shù)
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c API_AVAILABLE(macos(10.7), ios(6.0), tvos(9.0));

函數(shù)描述創(chuàng)建一個(gè)約束,該約束定義給定視圖的指定屬性之間的關(guān)系。約束表示形式為view1.attr1<relation> = multiplier × view2.attr2 + c 的線性方程。如果要表達(dá)的約束沒有第二個(gè)視圖和屬性,請(qǐng)使用nil和NSLayoutAttributeNotAnAttribute。

如果此方法用于創(chuàng)建無效約束,則該方法將引發(fā)NSInvalidArgumentException異常。例如這種我們無法描述的約束:view1.top = 0.0 x nil.NSLayoutAttributeNotAnAttribute + 200.0(視圖1的頂部等于0倍的不知道哪個(gè)視圖的哪個(gè)布局屬性加200,你什么都不知道,啊~,我崩潰了)或view1.top = 1.0 x view2.height + 20.0(視圖1的頂部等于1倍的視圖2的高度加20,我倆的確切關(guān)系是什么啊,啊~,我崩潰了)。

參數(shù) :

view1 :約束左側(cè)的視圖(要約束的視圖)。

attr1 :約束左側(cè)的視圖屬性。

relation :約束的左側(cè)和右側(cè)之間的關(guān)系。

view2 :約束右側(cè)的視圖(參照的視圖)。

attr2 :約束右側(cè)的視圖屬性。

multiplier:常數(shù)乘以約束右側(cè)的屬性,作為獲取修改屬性的一部分。

c:約束右側(cè)的乘以屬性值以后,添加約束值生成最終修改的屬性的常量。

返回值 : 用指定的關(guān)系、屬性、乘數(shù)和常量將兩個(gè)提供的視圖關(guān)聯(lián)起來的約束對(duì)象。

- (void)addConstraint:(NSLayoutConstraint *)constraint API_AVAILABLE(ios(6.0)); 

函數(shù)描述在調(diào)用函數(shù)的視圖或其子視圖的布局上添加約束。約束必須只涉及調(diào)用函數(shù)的視圖范圍內(nèi)的視圖。具體來說,涉及的任何視圖必須是調(diào)用函數(shù)的視圖本身,或者是調(diào)用函數(shù)的視圖的子視圖。添加到視圖中的約束被稱為該視圖持有的約束。在評(píng)估約束時(shí)使用的坐標(biāo)系統(tǒng)是持有約束的視圖的坐標(biāo)系統(tǒng)。

在為ios8.0或更高版本開發(fā)時(shí),將約束的active屬性設(shè)置為YES,而不是直接調(diào)用addConstraint:方法。active屬性會(huì)自動(dòng)在正確的視圖中添加和刪除約束。

參數(shù) :

constraint :要添加到視圖中的約束。約束只能引用視圖本身或其子視圖。

- (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints API_AVAILABLE(ios(6.0));

函數(shù)描述在調(diào)用函數(shù)的視圖或其子視圖的布局上添加多個(gè)約束。所有約束必須只涉及調(diào)用函數(shù)的視圖范圍內(nèi)的視圖。具體來說,涉及的任何視圖必須是調(diào)用函數(shù)的視圖本身,或者是調(diào)用函數(shù)的視圖的子視圖。添加到視圖中的約束稱為該視圖所持有。計(jì)算每個(gè)約束時(shí)使用的坐標(biāo)系是包含該約束的視圖的坐標(biāo)系。

在為iOS8.0或更高版本開發(fā)時(shí),可以使用NSLayoutConstraint類的activateConstraint:方法,而不是直接調(diào)用addConstraints:方法。activateConstraints:方法自動(dòng)將約束添加到正確的視圖中。

參數(shù) :

constraints : 要添加到視圖中的約束數(shù)組。所有約束只能引用視圖本身或其子視圖。

+ (void)activateConstraints:(NSArray<NSLayoutConstraint *> *)constraints API_AVAILABLE(macos(10.10), ios(8.0));

函數(shù)描述激活指定數(shù)組中的每個(gè)約束。這個(gè)方便的方法提供了一種簡單的方法來通過一次調(diào)用激活一組約束。此方法的效果與將每個(gè)約束的active屬性設(shè)置為YES相同。通常,使用此方法比單獨(dú)激活每個(gè)約束更有效

參數(shù):

constraints : 要激活的一組約束。

+ (void)deactivateConstraints:(NSArray<NSLayoutConstraint *> *)constraints API_AVAILABLE(macos(10.10), ios(8.0));

函數(shù)描述這是一種方便的方法,提供了一種簡單的方法來通過一次調(diào)用禁用一組約束。此方法的效果與將每個(gè)約束的active屬性設(shè)置為NO相同。通常,使用此方法比單獨(dú)禁用每個(gè)約束更有效

參數(shù):

constraints : 要禁用的一組約束。

+ (NSArray<NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *, id> *)metrics views:(NSDictionary<NSString *, id> *)views API_AVAILABLE(macos(10.7), ios(6.0), tvos(9.0));

函數(shù)描述 :創(chuàng)建由ASCII技術(shù)(如可視格式字符串)描述的約束。

參數(shù) :

format : 約束的格式規(guī)范。

opts : 描述視覺格式字符串中所有對(duì)象的屬性和布局方向的選項(xiàng)。

metrics : 出現(xiàn)在可視格式字符串中的常量字典。字典的鍵必須是視覺格式字符串中使用的字符串值。它們的值必須是NSNumber對(duì)象。

views : 以可視格式字符串顯示的視圖字典。鍵必須是視覺格式字符串中使用的字符串值,而值必須是視圖對(duì)象。

返回值 : 一個(gè)約束數(shù)組,組合在一起,表示所提供的視圖與其父視圖之間的約束,如可視化格式字符串所述。約束的返回順序與在可視格式字符串中指定的順序相同。

注 : 字符串約束的格式規(guī)范規(guī)則:

|   其含義表示父視圖
-   其含義表示距離
V:  其含義表示垂直
H:  其含義表示水平
>=  其含義表示視圖間距、寬度和高度必須大于或等于某個(gè)值
<=  其含義表示視圖間距、寬度和高度必須小宇或等于某個(gè)值
==  其含義表示視圖間距、寬度或者高度必須等于某個(gè)值
@   其含義表示>=、<=、==  其值限制最大設(shè)為1000
[view(>=200@300)]  其含義表示視圖的寬度為至少為200 不能超過  300
|-[view]-|  其含義表示視圖處在父視圖的左右邊緣內(nèi)
|-[view]    其含義表示視圖處在父視圖的左邊緣
|[view]     其含義表示視圖和父視圖左邊對(duì)齊
|-50.0-[view]-50.0-|  其含義表示離父視圖左右間距50
[view(200.0)]  其含義表示視圖寬度為 200.0
V:[view2(200.0)]  其含義表示視圖高度為 200.0

可以使用字符串來描述視圖間的約束,是不是很神奇,而且代碼設(shè)置約束的代碼是不是就減少了:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIView *view2 = [[UIView alloc]initWithFrame:CGRectZero];
    view2.backgroundColor = [UIColor greenColor];
    view2.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:view2];
    //描述view2距其父視圖頂部為0
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0.0-[view2](0.0)" options:0 metrics:nil views:@{@"view2": view2}]];
    //描述view2距其父視圖左右為0
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-0.0-[view2]-0.0-|" options:0 metrics:nil views:@{@"view2": view2}]];
    //描述view2高度為200
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[view2(200.0)]" options:0 metrics:nil views:@{@"view2": view2}]];

}

效果如圖 :

截屏2022-04-12 上午11.58.59.png

然鵝,但凡有一條約束使用字符串描述錯(cuò)誤,IOS就會(huì)讓你NSInvalidArgumentException異常了解一下,坑爹呢啊,我要掀桌了!!

NSLayoutAttribute - 布局屬性

NSLayoutAttribute,表示可視對(duì)象的一部分,應(yīng)該用于獲取約束的值。

typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
    //對(duì)象對(duì)齊矩形的左側(cè)
    NSLayoutAttributeLeft = 1,
    //對(duì)象對(duì)齊矩形的右側(cè)
    NSLayoutAttributeRight,
    //對(duì)象對(duì)齊矩形的頂部
    NSLayoutAttributeTop,
    //對(duì)象對(duì)齊矩形的底部
    NSLayoutAttributeBottom,
    //對(duì)象對(duì)齊矩形的前緣
    NSLayoutAttributeLeading,
    //對(duì)象對(duì)齊矩形的后緣
    NSLayoutAttributeTrailing,
    //對(duì)象對(duì)齊矩形的寬度
    NSLayoutAttributeWidth,
    //對(duì)象對(duì)齊矩形的高度
    NSLayoutAttributeHeight,
    //沿對(duì)象對(duì)齊矩形x軸的中心
    NSLayoutAttributeCenterX,
    //沿對(duì)象對(duì)齊矩形的y軸的中心
    NSLayoutAttributeCenterY,
    //對(duì)象的基線。對(duì)于具有多行文本的對(duì)象,這是最下面一行文本的基線。
    NSLayoutAttributeLastBaseline,
#if TARGET_OS_IPHONE
    NSLayoutAttributeBaseline NS_SWIFT_UNAVAILABLE("Use 'lastBaseline' instead") = NSLayoutAttributeLastBaseline,
#else
    NSLayoutAttributeBaseline = NSLayoutAttributeLastBaseline,
#endif
    //對(duì)象的基線。對(duì)于具有多行文本的對(duì)象,這是最上面一行文本的基線
    NSLayoutAttributeFirstBaseline API_AVAILABLE(macos(10.11), ios(8.0)),

#if TARGET_OS_IPHONE
    //對(duì)象的左邊距。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義
    NSLayoutAttributeLeftMargin API_AVAILABLE(ios(8.0)),
    //對(duì)象的右邊距。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義
    NSLayoutAttributeRightMargin API_AVAILABLE(ios(8.0)),
    //對(duì)象的上邊距。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義。
    NSLayoutAttributeTopMargin API_AVAILABLE(ios(8.0)),
    //對(duì)象的下邊距。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義。
    NSLayoutAttributeBottomMargin API_AVAILABLE(ios(8.0)),
    //對(duì)象的前緣邊距。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義
    NSLayoutAttributeLeadingMargin API_AVAILABLE(ios(8.0)),
    //對(duì)象的后緣邊距。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義
    NSLayoutAttributeTrailingMargin API_AVAILABLE(ios(8.0)),
    //對(duì)象左右邊距之間沿x軸的中心。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義。
    NSLayoutAttributeCenterXWithinMargins API_AVAILABLE(ios(8.0)),
    //對(duì)象上下邊距之間沿y軸的中心。對(duì)于UIView對(duì)象,頁邊距由其layoutMargins屬性定義。
    NSLayoutAttributeCenterYWithinMargins API_AVAILABLE(ios(8.0)),
#endif
    //占位符值,用于指示約束的第二項(xiàng)和第二個(gè)屬性在任何計(jì)算中都不使用。創(chuàng)建將常量指定給屬性的約束時(shí),請(qǐng)使用此值。
    //例如,item1.height>=40。如果約束只有一個(gè)項(xiàng),請(qǐng)將第二項(xiàng)設(shè)置為nil,并將第二個(gè)屬性設(shè)置為NSLayoutAttributeNotAnAttribute
    NSLayoutAttributeNotAnAttribute = 0
};

NSLayoutRelation - 布局關(guān)系

typedef NS_ENUM(NSInteger, NSLayoutRelation) {
    //約束要求第一個(gè)屬性小于或等于修改后的第二個(gè)屬性。
    NSLayoutRelationLessThanOrEqual = -1,
    //約束要求第一個(gè)屬性與修改后的第二個(gè)屬性完全相等。
    NSLayoutRelationEqual = 0,
    //約束要求第一個(gè)屬性大于或等于修改后的第二個(gè)屬性。
    NSLayoutRelationGreaterThanOrEqual = 1,
};

NSLayoutAnchor - 布局錨

使用流式 API 創(chuàng)建布局約束對(duì)象的工廠類。使用這些約束以編程方式使用 Auto Layout 定義布局。不要直接創(chuàng)建 NSLayoutConstraint 對(duì)象,而是從一個(gè)你想要約束的UIView、 NSView或者UILayoutGuide對(duì)象開始,然后選擇一個(gè)對(duì)象的錨定屬性。這些屬性對(duì)應(yīng)于 Auto Layout 中使用的主要 NSLayoutAttribute 值,并提供一個(gè)適當(dāng)?shù)腘SLayoutAnchor子類來創(chuàng)建該屬性的約束。使用錨定的方法來構(gòu)造約束。

NSLayoutAnchor類比直接使用NSLayoutConstraint的API創(chuàng)建約束代碼更簡潔,更容易閱讀。NSLayoutAttribute的子類提供了額外的類型檢查,在一定程度上防止創(chuàng)建無效的約束,但仍舊需要仔細(xì)檢查約束,以避免無效約束造成崩潰。

NSLayoutAnchor的常用函數(shù)
- (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor __attribute__((warn_unused_result));

函數(shù)描述返回一條約束,此方法定義兩個(gè)錨點(diǎn)相等(=)的關(guān)系。其中,調(diào)用此方法的錨點(diǎn)表示要布局的錨點(diǎn),參數(shù)錨點(diǎn)表示布局要參照的錨點(diǎn)。

參數(shù) :

anchor : 一個(gè)來自UIView、NSView或UILayoutGuide對(duì)象的布局錨。用來作為參照錨點(diǎn)。你必須使用NSLayoutAnchor的一個(gè)子類來匹配當(dāng)前的錨點(diǎn)。例如,如果在一個(gè)NSLayoutXAxisAnchor對(duì)象上調(diào)用這個(gè)方法,這個(gè)參數(shù)必須是另一個(gè)NSLayoutXAxisAnchor。

返回值 : 一個(gè)NSLayoutConstraint對(duì)象,它定義了兩個(gè)布局錨點(diǎn)之間的相等關(guān)系。

- (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor constant:(CGFloat)c __attribute__((warn_unused_result));

函數(shù)描述返回一條約束,此方法定義第一個(gè)錨點(diǎn)等于(=)第二個(gè)錨點(diǎn)加上常量偏移量的關(guān)系。其中,調(diào)用此方法的錨點(diǎn)表示要布局的錨點(diǎn),參數(shù)錨點(diǎn)表示布局要參照的錨點(diǎn)。值c表示恒定偏移量。

參數(shù) :

anchor : 一個(gè)來自UIView、NSView或UILayoutGuide對(duì)象的布局錨。用來作為參照錨點(diǎn)。你必須使用NSLayoutAnchor的一個(gè)子類來匹配當(dāng)前的錨點(diǎn)。例如,如果在一個(gè)NSLayoutXAxisAnchor對(duì)象上調(diào)用這個(gè)方法,這個(gè)參數(shù)必須是另一個(gè)NSLayoutXAxisAnchor。

c : 約束的恒定偏移量。

返回值 :一個(gè) NSLayoutConstraint對(duì)象,它定義了兩個(gè)布局錨點(diǎn)之間的相等關(guān)系與一個(gè)恒定偏移量。

- (NSLayoutConstraint *)constraintGreaterThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor __attribute__((warn_unused_result));

函數(shù)描述返回一條約束,此方法定義第一個(gè)錨點(diǎn)大于等于(>=)第二個(gè)錨點(diǎn)。其中,調(diào)用此方法的錨點(diǎn)表示要布局的錨點(diǎn),參數(shù)錨點(diǎn)表示布局要參照的錨點(diǎn)。

參數(shù) :

anchor : 一個(gè)來自UIView、NSView或UILayoutGuide對(duì)象的布局錨。用來作為參照錨點(diǎn)。你必須使用NSLayoutAnchor的一個(gè)子類來匹配當(dāng)前的錨點(diǎn)。例如,如果在一個(gè)NSLayoutXAxisAnchor對(duì)象上調(diào)用這個(gè)方法,這個(gè)參數(shù)必須是另一個(gè)NSLayoutXAxisAnchor。

返回值 :一個(gè) NSLayoutConstraint對(duì)象,它定義了布局錨點(diǎn)大于或等于參照錨點(diǎn)。

- (NSLayoutConstraint *)constraintGreaterThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor constant:(CGFloat)c __attribute__((warn_unused_result));

函數(shù)描述返回一條約束,此方法定義第一個(gè)錨點(diǎn)大于或等于(>=)第二個(gè)錨點(diǎn)加上常量偏移量。其中,調(diào)用此方法的錨點(diǎn)表示要布局的錨點(diǎn),參數(shù)錨點(diǎn)表示布局要參照的錨點(diǎn)。

參數(shù) :

anchor : 一個(gè)來自UIView、NSView或UILayoutGuide對(duì)象的布局錨。用來作為參照錨點(diǎn)。你必須使用NSLayoutAnchor的一個(gè)子類來匹配當(dāng)前的錨點(diǎn)。例如,如果在一個(gè)NSLayoutXAxisAnchor對(duì)象上調(diào)用這個(gè)方法,這個(gè)參數(shù)必須是另一個(gè)NSLayoutXAxisAnchor。

c : 約束的恒定偏移量。

返回值 :一個(gè) NSLayoutConstraint對(duì)象,它定義了兩個(gè)布局錨點(diǎn)之間大于或等于(>=)的關(guān)系與一個(gè)恒定偏移量。

- (NSLayoutConstraint *)constraintLessThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor __attribute__((warn_unused_result));

函數(shù)描述返回一條約束,此方法定義第一個(gè)錨點(diǎn)小于于或等于(<=)第二個(gè)錨點(diǎn)。其中,調(diào)用此方法的錨點(diǎn)表示要布局的錨點(diǎn),參數(shù)錨點(diǎn)表示布局要參照的錨點(diǎn)。

參數(shù) :

anchor : 一個(gè)來自UIView、NSView或UILayoutGuide對(duì)象的布局錨。用來作為參照錨點(diǎn)。你必須使用NSLayoutAnchor的一個(gè)子類來匹配當(dāng)前的錨點(diǎn)。例如,如果在一個(gè)NSLayoutXAxisAnchor對(duì)象上調(diào)用這個(gè)方法,這個(gè)參數(shù)必須是另一個(gè)NSLayoutXAxisAnchor。

返回值 :一個(gè) NSLayoutConstraint對(duì)象,它定義了布局錨點(diǎn)小于或等于參照錨點(diǎn)。

- (NSLayoutConstraint *)constraintLessThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor constant:(CGFloat)c __attribute__((warn_unused_result));

函數(shù)描述返回一條約束,此方法定義第一個(gè)錨點(diǎn)小于或等于(<=)第二個(gè)錨點(diǎn)加上常量偏移量。其中,調(diào)用此方法的錨點(diǎn)表示要布局的錨點(diǎn),參數(shù)錨點(diǎn)表示布局要參照的錨點(diǎn)。

參數(shù) :

anchor : 一個(gè)來自UIView、NSView或UILayoutGuide對(duì)象的布局錨。用來作為參照錨點(diǎn)。你必須使用NSLayoutAnchor的一個(gè)子類來匹配當(dāng)前的錨點(diǎn)。例如,如果在一個(gè)NSLayoutXAxisAnchor對(duì)象上調(diào)用這個(gè)方法,這個(gè)參數(shù)必須是另一個(gè)NSLayoutXAxisAnchor。

c : 約束的恒定偏移量。

返回值 :一個(gè) NSLayoutConstraint對(duì)象,它定義了兩個(gè)布局錨點(diǎn)之間小于或等于(<=)的關(guān)系與一個(gè)恒定偏移量。

小結(jié) :上面的方法分別定義了錨點(diǎn)間等于(=)、大于等于(>=)、小于等于(<=)的關(guān)系,以及是否有偏移量參與錨點(diǎn)間的定位,偏移量的數(shù)值均以點(diǎn)為單位測(cè)量。根據(jù)布局錨點(diǎn)的類型,偏移量的值可以以不同的方式進(jìn)行解釋

  • 對(duì)于NSLayoutXAxisAnchor對(duì)象,當(dāng)使用leadingAnchor(前緣)或trailingAnchor(后緣)時(shí),偏移量增加,錨點(diǎn)定位的視圖后移,偏移量減少,錨點(diǎn)定位的視圖前移。當(dāng)使用leftAnchor(左側(cè))或rightAnchor(右側(cè))時(shí),偏移量增加,錨點(diǎn)定位的視圖右移,偏移量減少,錨點(diǎn)定位的視圖左移。

  • 對(duì)于NSLayoutYAxisAnchor對(duì)象,當(dāng)使用topAnchor(頂部)或bottomAnchor(底部)時(shí),偏移量增加,錨點(diǎn)定位的視圖下移,偏移量減少,錨點(diǎn)定位的視圖上移。

  • 對(duì)于NSLayoutDimension對(duì)象,偏移量增加,錨點(diǎn)定位的視圖增大,偏移量減少,錨點(diǎn)定位的視圖減小。

NSLayoutXAxisAnchor - X軸布局錨

繼承自NSLayoutAnchor,NSLayoutXAxisAnchor會(huì)將類型信息添加到繼承自NSLayoutAnchor的方法中。具體來說,NSLayoutAnchor聲明的泛型方法現(xiàn)在必須接受一個(gè)匹配的NSLayoutXAxisAnchor對(duì)象,這在一定程度上防止創(chuàng)建無效的約束。

例如 :

// 此約束有效
[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor  constant: 8.0].active = true;
 
// 這個(gè)約束產(chǎn)生一個(gè)不兼容的指針類型警告
[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.topAnchor constant: 8.0].active = true;
NSLayoutXAxisAnchor的常用函數(shù)
- (NSLayoutDimension *)anchorWithOffsetToAnchor:(NSLayoutYAxisAnchor *)otherAnchor API_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0));

函數(shù)描述從兩個(gè)錨點(diǎn)創(chuàng)建布局尺寸對(duì)象。使用返回的對(duì)象來定義當(dāng)前錨點(diǎn)相對(duì)于和 otherAnchor 參數(shù)中的錨點(diǎn)對(duì)象之間的空間的約束。

參數(shù) :

otherAnchor : 創(chuàng)建布局尺寸時(shí)要使用的相對(duì)錨點(diǎn)。

返回值 : 由兩個(gè)錨表示的NSLayoutDimension對(duì)象。

NSLayoutYAxisAnchor - Y軸布局錨

繼承自NSLayoutAnchor,NSLayoutYAxisAnchor將類型信息添加到繼承自NSLayoutAnchor的方法中。具體來說,NSLayoutAnchor聲明的泛型方法現(xiàn)在必須接受一個(gè)匹配的NSLayoutYAxisAnchor對(duì)象,這在一定程度上防止創(chuàng)建無效的約束。

例如 :

// 此約束有效
[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor  constant: 8.0].active = true;
 
// 這個(gè)約束產(chǎn)生一個(gè)不兼容的指針類型警告
[self.cancelButton.topAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor constant: 8.0].active = true;
NSLayoutYAxisAnchor的常用函數(shù)
- (NSLayoutDimension *)anchorWithOffsetToAnchor:(NSLayoutYAxisAnchor *)otherAnchor API_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0));

函數(shù)描述從兩個(gè)錨點(diǎn)創(chuàng)建布局尺寸對(duì)象。使用返回的對(duì)象來定義當(dāng)前錨點(diǎn)相對(duì)于和 otherAnchor 參數(shù)中的錨點(diǎn)對(duì)象之間的空間的約束。

參數(shù) :

otherAnchor : 創(chuàng)建布局尺寸時(shí)要使用的相對(duì)錨點(diǎn)。

返回值 : 由兩個(gè)錨表示的NSLayoutDimension對(duì)象。

NSLayoutDimension - 布局尺寸

繼承自NSLayoutAnchor,除了為創(chuàng)建約束提供特定大小的方法外,這個(gè)類還將類型信息添加到繼承自NSLayoutAnchor的方法中。具體來說,由NSLayoutAnchor聲明的泛型方法現(xiàn)在必須接受一個(gè)匹配的NSLayoutDimension對(duì)象,這在一定程度上防止創(chuàng)建無效的約束。

例如 :

// 此約束有效
[self.saveButton.widthAnchor constraintEqualToAnchor:self.cancelButton.widthAnchor].active = YES;
 
// 這個(gè)約束產(chǎn)生一個(gè)不兼容的指針類型警告
[self.saveButton.widthAnchor constraintEqualToAnchor:self.cancelButton.leadingAnchor].active = YES;
NSLayoutDimension的常用函數(shù)
- (NSLayoutConstraint *)constraintEqualToConstant:(CGFloat)c __attribute__((warn_unused_result));

函數(shù)描述返回一條約束,該約束為錨定視圖的size屬性中寬或高定義為常量大小。這取決于視圖錨的類型。

參數(shù) :

c :一個(gè)常量,表示與此錨定視圖size屬性某一維度(寬或高)的關(guān)聯(lián)的屬性的大小。

返回值 :一個(gè)NSLayoutConstraint對(duì)象,它為與這個(gè)錨定視圖size屬性某一維度(寬或高)定義一個(gè)常量大小。

UIView (UIViewLayoutConstraintCreation) - 創(chuàng)建視圖布局約束

擴(kuò)展于UIView中的屬性為我們提供了創(chuàng)建約束的便利錨點(diǎn),如下 :

//表示視圖框架前緣的布局定位
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *leadingAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架后緣的布局定位
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *trailingAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架左側(cè)的布局定位
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *leftAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架右側(cè)的布局定位
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *rightAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架頂部的布局定位
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *topAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架底部的布局定位
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *bottomAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架寬
@property(nonatomic,readonly,strong) NSLayoutDimension *widthAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架高
@property(nonatomic,readonly,strong) NSLayoutDimension *heightAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架X軸中心點(diǎn)
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *centerXAnchor API_AVAILABLE(ios(9.0));
//表示視圖框架Y軸中心點(diǎn)
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *centerYAnchor API_AVAILABLE(ios(9.0));
//表示視圖中最頂行文本的基線
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *firstBaselineAnchor API_AVAILABLE(ios(9.0));
//表示視圖中文本最底行的基線。
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *lastBaselineAnchor API_AVAILABLE(ios(9.0));

\color{red}{此時(shí)我們可以更方便的為視圖添加約束了:}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIView *view2 = [[UIView alloc]initWithFrame:CGRectZero];
    view2.backgroundColor = [UIColor greenColor];
    view2.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:view2];
    
    [view2.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
    [view2.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
    [view2.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
    [view2.heightAnchor constraintEqualToConstant:200].active = YES;
}

\color{red}{我們也可以一次添加一組約束:}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIView *view2 = [[UIView alloc]initWithFrame:CGRectZero];
    view2.backgroundColor = [UIColor greenColor];
    view2.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:view2];
    
    [NSLayoutConstraint activateConstraints:@[
        [view2.topAnchor constraintEqualToAnchor:self.view.topAnchor],
        [view2.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
        [view2.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
        [view2.heightAnchor constraintEqualToConstant:200],
    ]];
}

豎屏?xí)r效果如下 :

截屏2022-04-13 上午11.39.18.png

橫屏?xí)r效果如下 :

截屏2022-04-13 上午11.39.32.png

\color{red}{我們還可以根據(jù)調(diào)整常數(shù)constant更改約束:}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIView *view1 = [[UIView alloc]initWithFrame:CGRectZero];
    view1.backgroundColor = [UIColor blueColor];
    view1.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:view1];
    
    [NSLayoutConstraint activateConstraints:@[
        [view1.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
        [view1.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
        [view1.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
        [view1.heightAnchor constraintEqualToConstant:200],
    ]];
    
    __block UIView *view2 = [[UIView alloc]initWithFrame:CGRectZero];
    view2.backgroundColor = [UIColor greenColor];
    view2.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:view2];
    
    [NSLayoutConstraint activateConstraints:@[
        [view2.topAnchor constraintEqualToAnchor:self.view.topAnchor],
        [view2.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
        [view2.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
        [view2.heightAnchor constraintEqualToConstant:200],
    ]];
    
    //獲取self.view上添加的約束
    NSArray<__kindof NSLayoutConstraint *> *constraints = view2.superview.constraints;
    //遍歷self.view上添加的約束
    [constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        //根據(jù)約束對(duì)象找到view2
        if ([obj.firstItem isEqual:view2]) {
            //找到view2的頂部約束
            if (obj.firstAttribute == NSLayoutAttributeTop) {
                //修改constant常量
                obj.constant = 50;
            }
            //獲取view2對(duì)象
            view2 = obj.firstItem;
            //獲取view2上添加的約束
            NSArray<__kindof NSLayoutConstraint *> *view2constraints = view2.constraints;
            //遍歷view2上添加的約束
            [view2constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull view2obj, NSUInteger idx, BOOL * _Nonnull stop) {
                ///找到view2的高度約束
                if (view2obj.firstAttribute == NSLayoutAttributeHeight) {
                    //修改constant常量
                    view2obj.constant = 100;
                }
            }];
        }
    }];
}

修改約束常數(shù)后效果如圖 :

截屏2022-04-14 上午10.30.27.png

UIView (UIConstraintBasedLayoutInstallingConstraints) -- 基于約束的布局安裝約束

擴(kuò)展自UIView,用于添加約束,約束通常安裝在視圖約束中涉及的最近的父視圖上。約束中的數(shù)字將在安裝該約束的視圖的坐標(biāo)系中進(jìn)行解釋。

UIView (UIConstraintBasedLayoutInstallingConstraints)的常用屬性
@property(nonatomic,readonly) NSArray<__kindof NSLayoutConstraint *> *constraints API_AVAILABLE(ios(6.0));

屬性描述 : 視圖所包含的約束。

UIView (UIConstraintBasedLayoutInstallingConstraints)的常用函數(shù)
//為視圖添加約束,在為iOS 8.0或更高版本開發(fā)時(shí),采用將約束的active屬性設(shè)置為YES。
- (void)addConstraint:(NSLayoutConstraint *)constraint API_AVAILABLE(ios(6.0)); 
//為視圖添加一組約束,在為iOS 8.0或更高版本開發(fā)時(shí),請(qǐng)使用NSLayoutConstraint類的activateConstraints:方法。
- (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints API_AVAILABLE(ios(6.0));
//為視圖移除指定的約束,在為iOS 8.0或更高版本開發(fā)時(shí),采用將約束的active屬性設(shè)置為NO。
- (void)removeConstraint:(NSLayoutConstraint *)constraint API_AVAILABLE(ios(6.0)); 
//為視圖移除指定的一組約束,在為iOS 8.0或更高版本開發(fā)時(shí),請(qǐng)使用NSLayoutConstraint類的deactivateConstraints:方法。
- (void)removeConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints API_AVAILABLE(ios(6.0)); 

UILayoutGuide - 布局占位

UILayoutGuide會(huì)創(chuàng)建一個(gè)用于占位的對(duì)象,它可以表示為一個(gè)矩形,并與自動(dòng)布局交互,它不會(huì)顯示在視圖層次結(jié)構(gòu)中,可以用來包含和封裝部分用戶界面,使復(fù)雜的頁面模塊化,簡化自動(dòng)布局約束邏輯。對(duì)比使用其它會(huì)顯示在視圖層次結(jié)構(gòu)中的對(duì)象作為占位圖,UILayoutGuide創(chuàng)建和維護(hù)成本更低,也更安全。使用init方法創(chuàng)建一個(gè)UILayoutGuide,然后用[UIView addLayoutGuide:]添加之后設(shè)置約束。

\color{red}{例如我們使用UILayoutGuide包含三個(gè)視圖放置在視圖中心 :}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UILayoutGuide *packGuide = [[UILayoutGuide alloc]init];
    [self.view addLayoutGuide:packGuide];
    [NSLayoutConstraint activateConstraints:@[
        [packGuide.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
        [packGuide.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
        [packGuide.widthAnchor constraintEqualToConstant:300],
        [packGuide.heightAnchor constraintEqualToConstant:300],
    ]];
    
    UIView *redView = [[UIView alloc]initWithFrame:CGRectZero];
    redView.backgroundColor = [UIColor redColor];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:redView];
    [NSLayoutConstraint activateConstraints:@[
        [redView.topAnchor constraintEqualToAnchor:packGuide.topAnchor],
        [redView.leadingAnchor constraintEqualToAnchor:packGuide.leadingAnchor],
        [redView.widthAnchor constraintEqualToConstant:150],
        [redView.heightAnchor constraintEqualToConstant:150],
    ]];
    
    UIView *greenView = [[UIView alloc]initWithFrame:CGRectZero];
    greenView.backgroundColor = [UIColor greenColor];
    greenView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:greenView];
    [NSLayoutConstraint activateConstraints:@[
        [greenView.topAnchor constraintEqualToAnchor:packGuide.topAnchor],
        [greenView.trailingAnchor constraintEqualToAnchor:packGuide.trailingAnchor],
        [greenView.widthAnchor constraintEqualToConstant:150],
        [greenView.heightAnchor constraintEqualToConstant:150],
    ]];
    
    UIView *yellowView = [[UIView alloc]initWithFrame:CGRectZero];
    yellowView.backgroundColor = [UIColor yellowColor];
    yellowView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:yellowView];
    [NSLayoutConstraint activateConstraints:@[
        [yellowView.centerXAnchor constraintEqualToAnchor:packGuide.centerXAnchor],
        [yellowView.bottomAnchor constraintEqualToAnchor:packGuide.bottomAnchor],
        [yellowView.widthAnchor constraintEqualToConstant:150],
        [yellowView.heightAnchor constraintEqualToConstant:150],
    ]];
    
}

UILayoutGuide放置在視圖上,但是是不顯示在圖層結(jié)構(gòu)中的:

截屏2022-04-14 下午4.52.06.png
截屏2022-04-14 下午4.54.11.png

UILayoutGuide同樣提供了布局使用的錨點(diǎn)屬性:

@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *leadingAnchor;
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *trailingAnchor;
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *leftAnchor;
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *rightAnchor;
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *topAnchor;
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *bottomAnchor;
@property(nonatomic,readonly,strong) NSLayoutDimension *widthAnchor;
@property(nonatomic,readonly,strong) NSLayoutDimension *heightAnchor;
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *centerXAnchor;
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *centerYAnchor;

SizeClasses(屏幕適配)

在Interface Builder(故事板)中使用,通過結(jié)合Auto Layout技術(shù),使設(shè)置的約束可以適應(yīng)不同設(shè)備的屏幕大小。蘋果將不同的設(shè)備在不同的狀態(tài)下,根據(jù)屏幕的寬高進(jìn)行了分類,屏幕的寬高分為Compact(緊湊)、Regular(常規(guī))類型與Any(任意)類型,然后可以根據(jù)屏幕寬高的分類,對(duì)視圖進(jìn)行約束,我們通過iPhone11為一個(gè)App設(shè)置啟動(dòng)圖觀察一下:

首先在LaunchScreen.storyboard中在設(shè)備豎屏的情況下,設(shè)置啟動(dòng)圖,如圖:

截屏2022-04-15 16.33.06.png

但當(dāng)屏幕橫屏?xí)r,啟動(dòng)圖就產(chǎn)生了問題,這顯然不是我們需要的,如圖:

截屏2022-04-15 16.39.31.png

我們先去觀察一下豎屏?xí)r,Xcode的設(shè)置面板:

截屏2022-04-15 16.08.18.png

我們將豎屏狀態(tài)下的約束進(jìn)行逐條的安裝,安裝操作如下:

截屏2022-04-15 16.34.00.png

之后我們將設(shè)置狀態(tài)改為橫屏,并取消默認(rèn)的約束安裝,此時(shí)視圖約束開始報(bào)錯(cuò),因?yàn)闄M屏狀態(tài)下,控件的約束還都未設(shè)置,需要我們重新添加。操作如下:

截屏2022-04-15 17.05.54.png
截屏2022-04-15 17.12.46.png

重新設(shè)置橫屏狀態(tài)下的約束后,橫屏?xí)r,啟動(dòng)圖展示如下 :

截屏2022-04-18 11.18.40.png

設(shè)置的約束一定要區(qū)分狀態(tài)進(jìn)行安裝,否則頁面容易錯(cuò)亂,然后我們對(duì)比一下不同狀態(tài)時(shí),安裝約束給出的默認(rèn)值:

豎屏?xí)r :

截屏2022-04-18 10.24.23.png

橫屏?xí)r:

截屏2022-04-18 11.20.41.png

我們可以對(duì)比出在iPhone11下,豎屏?xí)r,屏幕寬度是Compact(緊湊)的,高度是Regular(常規(guī))的,但設(shè)備橫屏?xí)r,寬度是Regular(常規(guī))的,高度是Compact(緊湊)的,根據(jù)這種分類,我們?cè)O(shè)置了視圖不同的約束。但是這些分類在不同的設(shè)備中是不一樣的,在iPhoneSE下,豎屏?xí)r,屏幕寬度是Compact(緊湊)的,高度是Regular(常規(guī))的,但設(shè)備橫屏?xí)r,屏幕寬度與高度是Compact(緊湊)的,而在ipad中,豎屏與橫屏?xí)r,寬度與高度都是Regular(常規(guī))的。這需要多種不同的約束讓視圖以不同的狀態(tài)適應(yīng)屏幕。

圖片資源也是有這些分類的,我們可以根據(jù)分類的不同設(shè)置不同的圖片,如圖:

截屏2022-04-18 11.43.39.png
截屏2022-04-18 11.45.35.png
截屏2022-04-18 11.46.07.png

豎屏?xí)r:

截屏2022-04-18 11.51.56.png

橫屏?xí)r:

截屏2022-04-18 11.51.34.png

添加約束的注意事項(xiàng)

  • 1.如果視圖的布局方式為autolayout,再添加約束之前要將視圖的translatesAutoresizingMaskIntoConstraints屬性設(shè)為NO,否則會(huì)約束沖突。

  • 2.確保將子視圖添加到父視圖后再添加約束,如果子視圖未添加到父視圖就添加約束么,會(huì)拋出NSInternalInconsistencyException異常,提示原因?yàn)闊o法設(shè)置視圖層次結(jié)構(gòu)未準(zhǔn)備好約束的布局。

添加約束選擇目標(biāo)View的規(guī)則

    1. 對(duì)于兩個(gè)同層級(jí)View之間的約束關(guān)系,添加到他們的父View上。
    1. 對(duì)于兩個(gè)不同層級(jí)View之間的約束關(guān)系,添加到他們最近的共同的父View上
    1. 對(duì)于有層次關(guān)系的兩個(gè)View之間的約束關(guān)系,添加到層次較高的父View上

至此,系統(tǒng)提供的糟糕的約束方法我們可以簡單的使用了,然后我們愉快的去使用Masonry,有封裝好的約束布局庫,和系統(tǒng)方法較什么勁呢??。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。