masonry更新xib約束探索

一個有趣的實驗

首先作個簡單的小實驗,在storyboard拖放一個view,添加好約束。以後利用masonry分別去更新這個視圖的位置,尺寸。會發現不同的結果。bash

//更新尺寸
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    
    [self.viewTest mas_updateConstraints:^(MASConstraintMaker *make) {
       //和storyboard中的約束相同
        make.width.mas_equalTo(375.0);
    }];
}
//更新位置
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    
    [self.viewTest mas_updateConstraints:^(MASConstraintMaker *make) {
       //和storyboard中的約束相同
        make.left.equalTo(self.viewTest.superview);
    }];
}
複製代碼

若是你這麼作會驚奇的發現更新位置有效,更新尺寸無效。why?到底用masonry能不能更新xib上的約束?帶着這個問題咱們去深刻探索。佈局

探索

第一步:查看源代碼

其實上面兩個小實驗控制檯都有打印警告 ui

在這裏插入圖片描述
大體意思是說約束衝突。分析控制檯中的兩個約束,第二個應該是xib上的約束(有個55常量),第一個是咱們更新後多出來的(MASLayout)。

那說明mas_updateConstraints致使多出來一個約束致使了約束衝突。讓咱們看看源代碼。spa

//調用代碼
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    //更新存在的約束
    constraintMaker.updateExisting = YES;
    block(constraintMaker);
    return [constraintMaker install];
}

//最終要執行的代碼
- (void)install {
    if (self.hasBeenInstalled) {
        return;
    }
    .......
     MASLayoutConstraint *existingConstraint = nil;
    //判斷是否更新存在約束(剛剛這裏賦值了YES)
    if (self.updateExisting) {
        //下邊去當前view上的約束裏去匹配傳過來的約束,若是相同返回這個約束,不然nil
        existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
    }
    //找到了匹配的約束
    if (existingConstraint) {
        // just update the constant
        //更新約束的值
        existingConstraint.constant = layoutConstraint.constant;
        self.layoutConstraint = existingConstraint;
    } else {
        //沒找到匹配約束就在當前view上添加外邊傳過來的約束
        [self.installedView addConstraint:layoutConstraint];
        self.layoutConstraint = layoutConstraint;
        [firstLayoutItem.mas_installedConstraints addObject:self];
    }
}

//遍歷匹配當前view上的約束
- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {
    // check if any constraints are the same apart from the only mutable property constant

    // go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints
    // and they are likely to be added first.
    /** 大體意思是倒序遍歷查找約束,過濾掉了auto-resizing,interface builder 上的約束,也就是說遍歷時候不考慮xib上的約束這樣碰到xib上的約束後就直接continue了,最終會找不到匹配的約束返回nil */
    for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) {
        if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue;
        if (existingConstraint.firstItem != layoutConstraint.firstItem) continue;
        if (existingConstraint.secondItem != layoutConstraint.secondItem) continue;
        if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue;
        if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue;
        if (existingConstraint.relation != layoutConstraint.relation) continue;
        if (existingConstraint.multiplier != layoutConstraint.multiplier) continue;
        if (existingConstraint.priority != layoutConstraint.priority) continue;

        return (id)existingConstraint;
    }
    return nil;
}
複製代碼

經過上面分析咱們知道了,xib上的約束是不去匹配的最終existingConstraint被賦值了nil。致使最終執行了[self.installedView addConstraint:layoutConstraint];新加了一個約束。而這個新的約束每每是最高優先級的,而且xib上的約束髮現默認也是最高優先級因此致使了約束衝突。控制檯打印了警報。code

第二步:驗證

步驟一分析得出的結論是mas_updateConstraints方法致使新增了一個同優先級的約束致使了約束衝突。那咱們嘗試去改一下xib上的相應約束的優先級,調低些。在運行代碼發現控制檯並無報錯輸出,同時不管更新尺寸仍是位置都有效。這也進一步驗證了步驟一的結論。cdn

總結

經過以上分析得出,masonry不能直接更新xib上的約束。若是咱們用mas_updateConstraints方法,masonry會新增一個約束,有可能會致使約束衝突,控制檯打印警報,視圖佈局錯亂。blog

那怎麼去更新xib上的約束的。有兩個方案,一是脫出來約束直接修改值。二是改變xib上的對應約束優先級,把優先級調低。而後用masonry去添加更高優先級的約束便可。相比較而言以爲方式一更好些。由於更改優先級其實就是要廢掉xib上的約束,不夠直接麻煩。圖片

疑問

一開始的兩個小實驗,由於masonry不能直接更新xib上的約束,致使mas_updateConstraints方法其實是新增了一個最高優先級的約束,致使約束衝突,控制檯打印警報。這能夠解釋通。ip

可是爲何一樣都是約束衝突,尺寸的約束衝突視圖界面會用xib的約束。位置的約束衝突視圖界面則用masonry的約束讓人費解。還有待探索。it

相關文章
相關標籤/搜索