首先作個簡單的小實驗,在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
那說明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