通過代碼添加約束之NSLayoutConstraint

什么是NSLayoutConstraint?

在xib中,我們可以用拖拽約束的方式來給空間添加約束條件,但是如果控件過多,則整個xib文件中的線條會變得混亂不堪,雖然蘋果在極力推薦可視化的加約束方式,但是還是給我們提供了代碼的方式來添加約束:NSLayoutConstraint。

如何使用NSLayoutConstraint?

在使用NSLayoutConstraint前,我們需要知道一個小的知識點:Autoresizing Mask。在使用 Auto Layout 時,首先需要將視圖的 setTranslatesAutoresizingMaskIntoConstraints屬性設置為 NO。這個屬性默認為 YES。當它為 YES 時,運行時系統(tǒng)會自動將 Autoresizing Mask 轉(zhuǎn)換為 Auto Layout 的約束,這些約束很有可能會和我們自己添加的產(chǎn)生沖突。 我們常常會忘了做這一步,然后引起的約束報錯就是這樣的:

沒有關閉setTranslatesAutoresizingMaskIntoConstraints屬性報的問題

在xib中,如果我們勾選了use auto layout,則編譯器會自動幫我們關閉Autoresizing Mask,如果是使用代碼添加約束,則需要手動關閉Autoresizing Mask。

setTranslatesAutoresizingMaskIntoConstraints這個方法是交給被添加約束的視圖來執(zhí)行的,關閉該視圖的Autoresizing Mask。在添加約束前,就應該關閉該屬性。

使用NSLayoutConstraint為視圖添加約束

Auto Layout 中約束對應的類為 NSLayoutConstraint,一個 NSLayoutConstraint 實例代表一條約束。

NSLayoutConstraint有兩個方法,我們主要介紹 constraintWithItem:也是最常用的:

+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

該方法有7個參數(shù),不過不用但心,這七個參數(shù)的聯(lián)系是很緊密的,前三個和被約束的視圖有關,后四個和他的父視圖有關。這個方法連起來可以翻譯如下:view1的某個屬性(attr1)等于view2的某個屬性(attr2)的值的多少倍(multiplier)加上某個常量(constant)。描述的是一個view與另外一個view的位置和大小約束關系。其中屬性attribute有上、下、左、右、寬、高等,關系relation有小于等于、等于、大于等于。需要注意的是,小于等于 或 大于等于 優(yōu)先會使用 等于 關系,如果 等于 不能滿足,才會使用 小于 或 大于。例如設置一個 大于等于100 的關系,默認會是 100,當視圖被拉伸時,100 無法被滿足,尺寸才會變得更大。

下邊我們上代碼,看看如何使用這個類來給視圖添加約束。

假如我們設計一個簡單的頁面。一個子view在父view中,其中子view的上下左右邊緣都離父view的邊緣40個像素。這個我們該如何寫呢?如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.view.backgroundColor = [UIColor yellowColor];
    
    
    UIView *subView = [[UIView alloc] init];
    subView.backgroundColor = [UIColor redColor];
    // 在設置約束前,先將子視圖添加進來
    [self.view addSubview:subView];
    
    // 使用autoLayout約束,禁止將AutoresizingMask轉(zhuǎn)換為約束
    [subView setTranslatesAutoresizingMaskIntoConstraints:NO];
    
    // 設置subView相對于VIEW的上左下右各40像素
    NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:40];
    NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:40];
    // 由于iOS坐標系的原點在左上角,所以設置右邊距使用負值
    NSLayoutConstraint *constraint3 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-40];
    
    // 由于iOS坐標系的原點在左上角,所以設置下邊距使用負值
    NSLayoutConstraint *constraint4 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-40];
    
    // 將四條約束加進數(shù)組中
    NSArray *array = [NSArray arrayWithObjects:constraint1, constraint2, constraint3, constraint4 ,nil];
    // 把約束條件設置到父視圖的Contraints中
    [self.view addConstraints:array];
}

然后我們看運行效果:

添加約束后的效果

這里有幾個注意點

  1. 添加約束前確定已經(jīng)把需要布局的子view添加到父view上了
  2. 一定要禁止將Autoresizing Mask轉(zhuǎn)換為約束
  3. 要把子view的約束加在父view上
  4. 因為iOS中原點在左上角所以使用offset時注意right和bottom用負數(shù)

我們再添加一個需求:
子view在父view的中間,且子view長200,高100。

    // ssubView在subView中間,寬高200,100
    UIView *ssubView = [[UIView alloc] init];
    ssubView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:ssubView];
    [ssubView setTranslatesAutoresizingMaskIntoConstraints:NO];
    
    // 加約束
    NSLayoutConstraint *sContraint1 = [NSLayoutConstraint constraintWithItem:ssubView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
    NSLayoutConstraint *sContraint2 = [NSLayoutConstraint constraintWithItem:ssubView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0];
    NSLayoutConstraint *sConstraint3 = [NSLayoutConstraint constraintWithItem:ssubView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:300];
    NSLayoutConstraint *sConstraint4 = [NSLayoutConstraint constraintWithItem:ssubView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200];
    
    NSArray *array2 = [NSArray arrayWithObjects:sContraint1, sContraint2,sConstraint3,sConstraint4, nil];
    [self.view addConstraints: array2];

查看運行效果:

添加新需求后的效果

這里有幾個注意點

  1. 如果是設置view自身的屬性,不涉及到與其他view的位置約束關系。比如view自身的寬、高等約束時,方法constraintWithItem:的第四個參數(shù)view2(secondItem)應設為nil;且第五個參數(shù)attire(secondAttribute)應設為NSLayoutAttributeNotAnAttribute 。
  2. 在設置寬和高這兩個約束時,relatedBy參數(shù)使用的是 NSLayoutRelationGreaterThanOrEqual,而不是 NSLayoutRelationEqual。因為 Auto Layout 是相對布局,所以通常你不應該直接設置寬度和高度這種固定不變的值,除非你很確定視圖的寬度或高度需要保持不變。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容