Masonry中的mas_makeConstraints方法

1、簡單介紹

咱們通常來講會這樣進行使用html

[objc]  view plain  copy
  1.  [view mas_makeConstraints:^(MASConstraintMaker *make) {  
  2.        //這個使用的就是MASCompositeConstraint類型的  
  3.         make.left.top.width.height.mas_equalTo(100).multipliedBy(1);  
  4.             //這個使用的就是單個單個的MASViewConstraint  
  5. //            make.left.mas_equalTo(100);  
  6. //            make.top.mas_equalTo(100);  
  7. //            make.width.mas_equalTo(100);  
  8. //            make.height.mas_equalTo(100);  
  9.     }];  
而後咱們先看看其內部的方法
[objc]  view plain  copy
  1. - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {  
  2.       
  3.     self.translatesAutoresizingMaskIntoConstraints = NO;  
  4.       
  5.     //建立MASConstraintMaker對象,就是咱們block中的make,  
  6.     MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];  
  7.       
  8.     //執行block  
  9.     block(constraintMaker);  
  10.       
  11.     //安裝install 約束  
  12.     return [constraintMaker install];  
  13. }  

咱們再去看看install的方法,其內部就是去先去獲取view的全部的約束,而後進行移除,以後再去安裝新的約束ios

[objc]  view plain  copy
  1. - (NSArray *)install {  
  2.  //判斷是否要移除已經存在的約束,這裏其實就是在使用re_make的時候這個會爲YES  
  3.  if (self.removeExisting) {  
  4.           
  5.         //獲取view的全部的約束  
  6.         NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];  
  7.           
  8.         for (MASConstraint *constraint in installedConstraints) {  
  9.               
  10.             [constraint uninstall];  
  11.         }  
  12.     }  
  13.       
  14.     //安裝新約束  
  15.     NSArray *constraints = self.constraints.copy;  
  16.       
  17.     for (MASConstraint *constraint in constraints) {  
  18.           
  19.         constraint.updateExisting = self.updateExisting;  
  20.           
  21.         [constraint install];  
  22.     }  
  23.     [self.constraints removeAllObjects];  
  24.       
  25.     return constraints;  
  26. }  

這個時候你們確定很疑惑,What? 我約束都尚未添加,怎麼直接開始遍歷了? 其實咱們在block中執行make.left.width這個時候其實就已經在添加約束了,先來看下在調用.left的時候調用的MASConstraintMaker的方法數組

返回值是MASConstriant,因此咱們在make.left以後再.width實際上是調用的是MASConstraint的width方法了其中這個方法是個抽象方法定義在MASConstraint類中。xcode

而後以後調用的就是MASViewConstraint中的addConstraintWithLayoutAttribute方法app

而後咱們點進上面的self.delegate 調用的方法進去看看,調用的是下面的方法,這裏其實就能夠理解爲把left和Width的約束進行合併成爲一個約束集合類oop

[objc]  view plain  copy
  1. - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {  
  2.      
  3.     //根據layoutAttribute屬性也就是NSLayoutAttributeTop這些初始化MASViewAttribute類  
  4.     MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];  
  5.       
  6.     //根據viewAttribute建立MASViewConstraint對象  
  7.     MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];  
  8.     //若是有constraint的話,就進行合併成一個composite Constraint,這個是一個集合類  
  9.     if ([constraint isKindOfClass:MASViewConstraint.class]) {  
  10.         //replace with composite constraint  
  11.         NSArray *children = @[constraint, newConstraint];  
  12.           
  13.         /* 
  14.          MASCompositeConstraint:約束的集合類。內部有一個數組,能夠保存多個MASViewConstraint。對MASCompositeConstraint調用方法實際等於 
  15.          對其內部的全部MASViewConstraint調用方法 
  16.          */  
  17.         MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];  
  18.           
  19.         compositeConstraint.delegate = self;  
  20.           
  21.         //在self.constraint進行替換  
  22.         [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];  
  23.           
  24.         return compositeConstraint;  
  25.     }  
  26.     //若是不存在constraint那就去設置newConstraint,而後添加進入約束數組  
  27.     if (!constraint) {  
  28.         newConstraint.delegate = self;  
  29.         //add  
  30.         [self.constraints addObject:newConstraint];  
  31.     }  
  32.     return newConstraint;  
  33. }  

再去看看constraintMaker中的install方法中的NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];裏面的installedConstraintsForView方法內部調用了mas_installedConstraints的get方法,而後去獲取全部的對象,也就是這裏面的方法佈局

[objc]  view plain  copy
  1. + (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {  
  2.     return [view.mas_installedConstraints allObjects];  
  3. }  

再去看看這個get方法其實就是運用了運行時的知識post

[objc]  view plain  copy
  1. - (NSMutableSet *)mas_installedConstraints {  
  2.       
  3.     NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey);  
  4.       
  5.     if (!constraints) {  
  6.           
  7.         constraints = [NSMutableSet set];  
  8.           
  9.         //至關於 setValue:forKey 進行關聯value對象  
  10.         objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC);  
  11.     }  
  12.     return constraints;  
  13. }  

而後再去看看關於uninstall方法的實現,這個方法的實現就是先判斷能不能相應active方法,由於這個屬性是ios8纔出現的flex

[objc]  view plain  copy
  1. - (void)uninstall {  
  2.       
  3.     if ([self supportsActiveProperty]) {  
  4.         //設置約束不可用  
  5.         self.layoutConstraint.active = NO;  
  6.         //移除約束  
  7.         [self.firstViewAttribute.view.mas_installedConstraints removeObject:self];  
  8.           
  9.         return;  
  10.     }  
  11.     //來到下面表示不能響應active方法,作了適配  
  12.     [self.installedView removeConstraint:self.layoutConstraint];  
  13.       
  14.     self.layoutConstraint = nil;  
  15.       
  16.     self.installedView = nil;  
  17.       
  18.     [self.firstViewAttribute.view.mas_installedConstraints removeObject:self];  
  19. }  

 

咱們再去看看安裝新約束當中的install方法ui

[objc]  view plain  copy
  1. - (void)install {  
  2.       
  3.     //判斷約束是否已經存在,以及是否已經處理激活狀態其實就是使用狀態  
  4.     if (self.hasBeenInstalled) {  
  5.         return;  
  6.     }  
  7.       
  8.     //判斷佈局是否能夠響應active這個方法的設置,判斷layoutConstriant存不存在  
  9.     if ([self supportsActiveProperty] && self.layoutConstraint) {  
  10.           
  11.         //iOS 6.0或者7.0調用addConstraints  
  12.         //[self.view addConstraints:@[leftConstraint, rightConstraint, topConstraint, heightConstraint]];  
  13.         //iOS 8.0之後設置active屬性值 就能夠去使用約束了  
  14.         self.layoutConstraint.active = YES;  
  15.           
  16.         //這裏會進行添加約束  
  17.         [self.firstViewAttribute.view.mas_installedConstraints addObject:self];  
  18.           
  19.         return;  
  20.     }  
  21.     //獲取item,獲取第一個viewattribute的item也就是constraintWithItem中的item  
  22.     MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;  
  23.       
  24.     //獲取屬性好比說NSLayoutAttributeTop  
  25.     NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;  
  26.       
  27.     //獲取約束的第二個view  
  28.     MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;  
  29.       
  30.     //獲取layout的屬性  
  31.     NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;  
  32.   
  33.     // alignment attributes must have a secondViewAttribute  
  34.     // therefore we assume that is refering to superview  
  35.     // eg make.left.equalTo(@10)  
  36.       
  37.     //判斷是否是要設置的是size的約束,以及判斷第二個約束的屬性是否是爲空若是爲空,就去設置下面的屬性  
  38.     if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {  
  39.         secondLayoutItem = self.firstViewAttribute.view.superview;  
  40.         secondLayoutAttribute = firstLayoutAttribute;  
  41.     }  
  42.     //建立約束佈局 self.layoutRelation就是約束關係  
  43.     MASLayoutConstraint *layoutConstraint  
  44.         = [MASLayoutConstraint constraintWithItem:firstLayoutItem  
  45.                                         attribute:firstLayoutAttribute  
  46.                                         relatedBy:self.layoutRelation  
  47.                                            toItem:secondLayoutItem  
  48.                                         attribute:secondLayoutAttribute  
  49.                                        multiplier:self.layoutMultiplier  
  50.                                          constant:self.layoutConstant];  
  51.       
  52.     //設置約束的優先級  
  53.     layoutConstraint.priority = self.layoutPriority;  
  54.       
  55.     /* 
  56.      當約束衝突發生的時候,咱們能夠設置view的key來定位是哪一個view 
  57.      redView.mas_key = @"redView"; 
  58.      greenView.mas_key = @"greenView"; 
  59.      blueView.mas_key = @"blueView"; 
  60.      如果以爲這樣一個個設置比較繁瑣,怎麼辦呢,Masonry則提供了批量設置的宏MASAttachKeys 
  61.      MASAttachKeys(redView,greenView,blueView); //一句代碼便可所有設置 
  62.      */  
  63.     layoutConstraint.mas_key = self.mas_key;  
  64.       
  65.     //判斷第二個view是否存在  
  66.     if (self.secondViewAttribute.view) {  
  67.           
  68.         //尋找兩個視圖的公共父視圖,這個方法其實就是循環遍歷尋找  
  69.         MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];  
  70.         //斷言判斷公共父視圖存不存在  
  71.         NSAssert(closestCommonSuperview,  
  72.                  @"couldn't find a common superview for %@ and %@",  
  73.                  self.firstViewAttribute.view, self.secondViewAttribute.view);  
  74.         //設置安裝約束的視圖  
  75.         self.installedView = closestCommonSuperview;  
  76.     }  
  77.     //若是是設置view的寬度和高度的,設置安裝越蘇的視圖爲第一個view  
  78.     else if (self.firstViewAttribute.isSizeAttribute) {  
  79.         self.installedView = self.firstViewAttribute.view;  
  80.     }  
  81.     //不然就給superview進行設置  
  82.     else {  
  83.         self.installedView = self.firstViewAttribute.view.superview;  
  84.     }  
  85.   
  86.   
  87.     MASLayoutConstraint *existingConstraint = nil;  
  88.       
  89.     //判斷是不是更新約束,這裏判斷的條件就是是否只存在constant不同的視圖  
  90.     if (self.updateExisting) {  
  91.         existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];  
  92.     }  
  93.     //若是已經存在只有constant不同的約束,就去更新constant  
  94.     if (existingConstraint) {  
  95.         // just update the constant  
  96.         existingConstraint.constant = layoutConstraint.constant;  
  97.           
  98.         self.layoutConstraint = existingConstraint;  
  99.     }  
  100.     //若是沒有存在的只有constant不同的約束,就去添加約束  
  101.     else {  
  102.         [self.installedView addConstraint:layoutConstraint];  
  103.           
  104.         self.layoutConstraint = layoutConstraint;  
  105.           
  106.         [firstLayoutItem.mas_installedConstraints addObject:self];  
  107.     }  
  108. }  

關於上面判斷是否只存在constant不同的視圖的方法

[objc]  view plain  copy
  1. - (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {  
  2.     // check if any constraints are the same apart from the only mutable property constant  
  3.   
  4.     // go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints  
  5.     // and they are likely to be added first.  
  6.       
  7.     //從後面往前面遍歷這個數組  
  8.     for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) {  
  9.         if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue;  
  10.         if (existingConstraint.firstItem != layoutConstraint.firstItem) continue;  
  11.         if (existingConstraint.secondItem != layoutConstraint.secondItem) continue;  
  12.         if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue;  
  13.         if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue;  
  14.         if (existingConstraint.relation != layoutConstraint.relation) continue;  
  15.         if (existingConstraint.multiplier != layoutConstraint.multiplier) continue;  
  16.         if (existingConstraint.priority != layoutConstraint.priority) continue;  
  17.   
  18.         return (id)existingConstraint;  
  19.     }  
  20.     return nil;  
  21. }  

關於self.hasBeenInstalled其實就是調用了下面的方法

[objc]  view plain  copy
  1. - (BOOL)hasBeenInstalled {  
  2.     return (self.layoutConstraint != nil) && [self isActive];  
  3. }  

其實Masonry就是對系統自己的自動佈局的layout進行了封裝

相關文章
相關標籤/搜索