iOS學習——佈局利器Masonry框架源碼深度剖析

来源:https://www.cnblogs.com/mukekeheart/archive/2018/02/12/8431899.html
-Advertisement-
Play Games

iOS開發過程中很大一部分內容就是界面佈局和跳轉,iOS的佈局方式也經歷了 顯式坐標定位方式 --> autoresizingMask --> iOS 6.0推出的自動佈局(Auto Layout)的逐步優化,至於為什麼推出自動佈局,肯定是因為之前的方法不好用(哈哈 簡直是廢話),具體如何不好用以及 ...


  iOS開發過程中很大一部分內容就是界面佈局和跳轉,iOS的佈局方式也經歷了 顯式坐標定位方式 --> autoresizingMask --> iOS 6.0推出的自動佈局(Auto Layout)的逐步優化,至於為什麼推出自動佈局,肯定是因為之前的方法不好用(哈哈 簡直是廢話),具體如何不好用以及怎麼變化大家可以瞅瞅 這篇文章iOS6.0推出的自動佈局實際上用佈局約束(Layout Constraint)來實現,通過佈局約束(Layout Constraint)可以確定兩個視圖之間精確的位置的相對距離,為此,iOS6.0推出了NSLayoutConstraint來定義約束,使用方法如下:

[NSLayoutConstraint constraintWithItem:view1
  attribute:NSLayoutAttributeLeft
  relatedBy:NSLayoutRelationEqual
  toItem:view2
  attribute:NSLayoutAttributeRight
  multiplier:1
  constant:10];

//翻譯過來就是:view1的左側,在,view2的右側,再多10個點,的地方。

佈局約束的添加規則:

(1)對於兩個同層級 view 之間的約束關係,添加到它們的父 view 上
(2)對於兩個不同層級 view 之間的約束關係,添加到他們最近的共同父 view 上
(3)對於有層次關係的兩個 view 之間的約束關係,添加到層次較高的父 view 上
(4)對於比如長寬之類的,只作用在該 view 自己身上的話,添加到該 view 自己上

  具體關於NSLayoutConstraint的詳細使用方法參見:NSLayoutConstraint-代碼實現自動佈局。今天我們文章的主角——Masonry框架實際上是在NSLayoutConstraint的基礎上進行封裝的,這一點在後面的源碼分析中我們詳細解釋。

1 Masonry的佈局教程

  當我們需要對控制項的top,bottom,left,right進行約束就特別麻煩,在OC中有一個庫MasonryNSLayoutConstraint進行了封裝,簡化了約添加約束的方式和流程。用Masonry框架進行佈局非常簡單,主要特點是採用鏈式語法進行佈局,這一點使得我們在使用和代碼佈局上更為方便,利用Masonry進行佈局的前提條件之一是 佈局視圖必須先被添加到父視圖中簡單示例如下代碼,關於Masonry框架的使用並不是本文的重點,詳情可以參見:Masonry介紹與使用實踐:快速上手Autolayout。如果你的項目是Swift語言的,那麼就得使用SnapKit佈局框架了,SnapKit其實就是Masonry的Swift版本,兩者雖然實現語言不同,但是實現思路大體一致。

UIView *sv1 = [UIView new];
//利用Masonry進行佈局的前提條件之一是 佈局視圖必須先被添加到父視圖中
[sv addSubview:sv1];
[sv1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
     
    /* 等價於
    make.top.equalTo(sv).with.offset(10);
    make.left.equalTo(sv).with.offset(10);
    make.bottom.equalTo(sv).with.offset(-10);
    make.right.equalTo(sv).with.offset(-10);
    */
     
    /* 也等價於
    make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
    */
}];

 2 Masonry框架源碼分析

  Masonry框架是在NSLayoutConstraint的基礎上進行封裝的,其涉及到的內容也是非常繁多。在進行源碼剖析時我們從我們經常用到的部分出發,一層一層進行解析和研究。

2.1 調用流程分析

  首先,我們先大體瞭解一下調用 mas_makeConstraints 進行佈局時的流程步驟,其實另外兩個 mas_updateConstraints 和 mas_remakeConstraints 的流程也基本上是一樣的。

  •  - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block 是Masonry框架中UIview + MASAdditionsUIview分類)中的方法,所以一般的控制項視圖都可以直接調用該方法,該方法傳入一個block函數作為參數(返回值為void,參數為MASContraintMaker的實例對象make)
  • 主要佈局方法 - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block 的源碼和解析如下,主要工作是創建一個約束創建器,並將其傳到block中(其實就是block中的make創建器)進行創建約束並返回
    @implementation MAS_VIEW (MASAdditions)
    
    - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
        //關閉AutoresizingMask的佈局方法是我們進行Auto Layout佈局的前提步驟
        self.translatesAutoresizingMaskIntoConstraints = NO;
        //創建一個約束創建器
        MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
        //在block中配置constraintMaker對象,即將constraintMaker傳入block中(其實就是我們在block中用來添加約束的make)進行約束配置
        block(constraintMaker);
        //約束安裝並以數組形式返回
        return [constraintMaker install];
    }
    
    ...
  • 約束安裝方法 [constraintMaker install]; 的源代碼如下,這部分的代碼很簡單,主要就是對當前約束創建器中的約束進行更新,因為除了我們這個 mas_makeConstraints 方法中會調用該方法之外, mas_updateConstraints 和 mas_remakeConstraints 中都會調用該方法進行約束的安裝,所以在該約束安裝方法中考慮了約束的刪除和是否有更新等情況的處理。

    //install方法主要就是對下麵這個約束數組進行維護
    @property (nonatomic, strong) NSMutableArray *constraints;
    
    - (NSArray *)install {
        //判斷是否有要刪除的約束,有則逐個刪除
        if (self.removeExisting) {
            NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
            for (MASConstraint *constraint in installedConstraints) {
                [constraint uninstall];
            }
        }
        //更新約束
        NSArray *constraints = self.constraints.copy;
        for (MASConstraint *constraint in constraints) {
            constraint.updateExisting = self.updateExisting;
            //約束安裝(這個才是真正的添加約束的方法
            [constraint install];
        }
        [self.constraints removeAllObjects];
        return constraints;
    }
  • 上面這段代碼中真正添加約束的方法其實是 [constraint install]; ,這裡我們要分析一下這個install到底調用的是哪個方法的install?因為這裡有好幾個類(MASConstraint、MASViewConstraint、MASCompositeConstraint)有install方法。要知道具體調用的是哪一個類的install方法,我們就要弄清楚這裡的約束constraint到底是什麼類型,這就需要我們瞭解約束創建器(MASConstraintMaker)中的約束數組constraints中添加的到底是什麼類型的約束,經過分析(分析過程在後面會講到)我們發現這裡添加的約束是MASViewConstraint類型的,根據面向對象的多態特性,所以我們這裡調用的其實就是MASViewConstraint的install方法,該方法關鍵代碼(代碼太長,只放關鍵性代碼)如下,我們可以看到其實就是通過iOS系統自帶的自動佈局約束佈局類NSLayoutConstraint進行佈局
    - (void)install {
        if (self.hasBeenInstalled) {
            return;
        }
    
        ...
    
        //MASLayoutConstraint其實就是在NSLayoutConstraint基礎上添加了一個屬性而已
        //@interface MASLayoutConstraint : NSLayoutConstraint
        
        MASLayoutConstraint *layoutConstraint
            = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                            attribute:firstLayoutAttribute
                                            relatedBy:self.layoutRelation
                                               toItem:secondLayoutItem
                                            attribute:secondLayoutAttribute
                                           multiplier:self.layoutMultiplier
                                             constant:self.layoutConstant];
        layoutConstraint.priority = self.layoutPriority;
        layoutConstraint.mas_key = self.mas_key;
        
        ...
    
        //添加約束
        if (existingConstraint) {
            // just update the constant
            existingConstraint.constant = layoutConstraint.constant;
            self.layoutConstraint = existingConstraint;
        } else {
            [self.installedView addConstraint:layoutConstraint];
            self.layoutConstraint = layoutConstraint;
            [firstLayoutItem.mas_installedConstraints addObject:self];
        }
    }

   通過上面的分析和研究,我們基本上已經把Masonry框架中主要佈局方法的主流程瞭解清楚了。因為這是第一次學習iOS第三方框架的源碼,在這個學習過程中也走了很多彎路,最開始是從最基本的類開始看,後來發現越看越不懂,不知道這個屬性的定義在什麼時候用到,是什麼含義((ノへ ̄、)捂臉。。。)。後來通過摸索才知道源碼學習應該直接從用到的方法著手,然後一步一步深入分析源碼中每一步的目的和意義,順藤摸瓜,逐個擊破

 2.2 Masonry框架中的鏈式語法

  下麵的代碼是比較常用的幾種Masonry的佈局格式,我們可以看到都是通過點語法的鏈式調用進行佈局的。之前在學習Java和Android的過程中接觸過鏈式語法,在Java中要實現這種鏈式語法很簡單,無非就是每個方法的返回值就是其本身,因為Java的方法調用是通過點語法調用的,所以很容易實現。但是在OC中,方法調用都是通過  [clazz method:parm];  的形式進行調用的,那麼Masonry框架中是怎麼實現的呢?

make.top.equalTo(sv).with.offset(10);

make.left.right.mas_equalTo(sv).mas_offset(0.0f);

make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));

   同樣的學習方法,我們來看一下源碼中各個屬性或方法是怎麼實現的,最重要的原因就是getter方法和Objective-C 裡面,調用方法是可以使用點語法的,但這僅限於沒有參數的方法

  • 首先,我們在用Masonry進行佈局的時候最先用MASConstraintMaker調用一個方位屬性(在MASConstraintMaker中定義了許多方位屬性進行初始化調用,具體有哪些如下MASConstraintMaker.h文件中所示),用點語法調用,例如 make.top ,這時候其實是調用了其getter方法,然後在getter方法中對該約束的代理進行設置(見下MASConstraintMaker.m文件中標紅註釋處)
    //MASConstraintMaker.h文件
    @interface MASConstraintMaker : NSObject
    
    @property (nonatomic, strong, readonly) MASConstraint *left;
    @property (nonatomic, strong, readonly) MASConstraint *top;
    @property (nonatomic, strong, readonly) MASConstraint *right;
    @property (nonatomic, strong, readonly) MASConstraint *bottom;
    @property (nonatomic, strong, readonly) MASConstraint *leading;
    @property (nonatomic, strong, readonly) MASConstraint *trailing;
    @property (nonatomic, strong, readonly) MASConstraint *width;
    @property (nonatomic, strong, readonly) MASConstraint *height;
    @property (nonatomic, strong, readonly) MASConstraint *centerX;
    @property (nonatomic, strong, readonly) MASConstraint *centerY;
    @property (nonatomic, strong, readonly) MASConstraint *baseline;
    
    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
    
    @property (nonatomic, strong, readonly) MASConstraint *firstBaseline;
    @property (nonatomic, strong, readonly) MASConstraint *lastBaseline;
    
    #endif
    
    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
    
    @property (nonatomic, strong, readonly) MASConstraint *leftMargin;
    @property (nonatomic, strong, readonly) MASConstraint *rightMargin;
    @property (nonatomic, strong, readonly) MASConstraint *topMargin;
    @property (nonatomic, strong, readonly) MASConstraint *bottomMargin;
    @property (nonatomic, strong, readonly) MASConstraint *leadingMargin;
    @property (nonatomic, strong, readonly) MASConstraint *trailingMargin;
    @property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins;
    @property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins;
    
    #endif
    
    @property (nonatomic, strong, readonly) MASConstraint *edges;
    @property (nonatomic, strong, readonly) MASConstraint *size;
    @property (nonatomic, strong, readonly) MASConstraint *center;
    ...
    
    @end
    
    
    //MASConstraintMaker.m文件
    @implementation MASConstraintMaker
    
    //每個方法返回的也是MASConstraint對象,實際上是MASViewConstraint、MASCompositeConstraint類型的對象,見最後的函數中標紅的註釋
    - (MASConstraint *)top {
        //將對應的系統自帶的約束佈局的屬性NSLayoutAttributeTop傳入
        return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
    }
    
    //過渡方法
    - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
    }
    
    //最終的調用
    - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
        MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
        
      ...
    
        //添加約束
        if (!constraint) {
            //設置約束的代理是self
            newConstraint.delegate = self;
            [self.constraints addObject:newConstraint];
        }
        //返回MASViewConstraint類型的對象
        return newConstraint;
    }
    
    ...
    
    @end
  • 然後通過第一步的初始化之後返回的就是一個MASViewConstraint對象了,後面的點語法主要就在於MASViewConstraint中的屬性和方法了,在MASViewConstraint的.h和.m文件中我們都沒有找到top等方位相關的屬性或方法,但是我們發現MASViewConstraint是繼承自MASConstraint的,然後我們發現在MASConstraint中定義了大量的方位相關方法(如下代碼所示),所以類似 make.top.left 前面一個top是調用MASConstraintMaker的方法,後面一個left則是通過點語法調用MASConstraint的方法。但是為什麼這些方法可以進行點語法調用呢?原因就是在Objective-C 裡面,調用方法是可以使用點語法的,但這僅限於沒有參數的方法
    @interface MASViewConstraint : MASConstraint <NSCopying>
    //MASConstraint.h文件
    
    @interface MASConstraint : NSObject
    
    /**
     *    Creates a new MASCompositeConstraint with the called attribute and reciever
     */
    - (MASConstraint *)left;
    - (MASConstraint *)top;
    - (MASConstraint *)right;
    - (MASConstraint *)bottom;
    - (MASConstraint *)leading;
    - (MASConstraint *)trailing;
    - (MASConstraint *)width;
    - (MASConstraint *)height;
    - (MASConstraint *)centerX;
    - (MASConstraint *)centerY;
    - (MASConstraint *)baseline;
    
    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
    
    - (MASConstraint *)firstBaseline;
    - (MASConstraint *)lastBaseline;
    
    #endif
    
    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
    
    - (MASConstraint *)leftMargin;
    - (MASConstraint *)rightMargin;
    - (MASConstraint *)topMargin;
    - (MASConstraint *)bottomMargin;
    - (MASConstraint *)leadingMargin;
    - (MASConstraint *)trailingMargin;
    - (MASConstraint *)centerXWithinMargins;
    - (MASConstraint *)centerYWithinMargins;
    
    #endif
    
    ...
    
    @end
    //MASConstraint.m文件
    - (MASConstraint *)top {
        //這裡會調用MASViewConstraint中的addConstraintWithLayoutAttribute:方法
        return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
    }
    
    //MASViewConstraint.m文件
    - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
        //調用代理的方法,之前我們說過設置的代理是MASConstraintMaker對象make,所以調用的實際上是MASConstraintMaker添加約束的方法,這就是我們再上面第一步講到的方法
        return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
    }
    
    //MASConstraintMaker.m文件
    - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
        MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
        //當傳入的constraint不為空時,即此調用不是第一個,make.toip.left在left時的調用
        if ([constraint isKindOfClass:MASViewConstraint.class]) {
            //則用MASCompositeConstraint作為返回值,即組約束
            NSArray *children = @[constraint, newConstraint];
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            //設置代理
            compositeConstraint.delegate = self;
            //重新設置
            [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
           //返回 MASCompositeConstraint對象return compositeConstraint;
        }
    
        if (!constraint) {
            //設置代理
            newConstraint.delegate = self;
            //設置約束
            [self.constraints addObject:newConstraint];
        }
    
        return newConstraint;
    }

2.3 鏈式語法中傳參方法的調用 

  在上一小節我們提到了鏈式語法的主要原因在於在Objective-C 裡面,調用方法是可以使用點語法的,但這僅限於沒有參數的方法,但是類似mas_equalTo、mas_offset等帶參數傳遞的方法依舊可以用鏈式語法又是怎麼一回事呢最關鍵的一環就是 blockblock就是一個代碼塊,但是它的神奇之處在於在內聯(inline)執行的時候還可以傳遞參數。同時block本身也可以被作為參數在方法和函數間傳遞。block作為參數傳遞很常見,就是在我們的Masonry框架中添加約束的方法 - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block 中就是講一個block作為參數進行傳遞的。

  同樣在MASConstraint中,我們可以看到mas_equalTo、mas_offset等帶參方法的定義如下,我們可以看到,方法的定義中並沒有參數,但是返回值是一個帶參的block,並且該block還返回一個MASConstraint對象(MASViewConstraint或者MASCompositeConstraint對象),所以方法的定義和使用都沒有什麼問題,和上一小節分析的內容差不多。最主要的區別就是這裡返回值為帶參數的block,並且該block的參數可以通過我們的方法進行傳值。關於帶參block作為返回值得用法可以參見 此鏈接的文章

- (MASConstraint * (^)(MASEdgeInsets insets))insets;

- (MASConstraint * (^)(CGFloat inset))inset;

- (MASConstraint * (^)(CGSize offset))sizeOffset;

- (MASConstraint * (^)(CGPoint offset))centerOffset;

- (MASConstraint * (^)(CGFloat offset))offset;

- (MASConstraint * (^)(NSValue *value))valueOffset;

- (MASConstraint * (^)(CGFloat multiplier))multipliedBy;

- (MASConstraint * (^)(CGFloat divider))dividedBy;

- (MASConstraint * (^)(MASLayoutPriority priority))priority;

- (MASConstraint * (^)(void))priorityLow;

- (MASConstraint * (^)(void))priorityMedium;

- (MASConstraint * (^)(void))priorityHigh;

- (MASConstraint * (^)(id attr))equalTo;

- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;
//MASConstraint.m文件
- (MASConstraint * (^)(id))mas_equalTo {
    return ^id(id attribute) {
        //多態調用子類MASViewConstraint或者MASCompositeConstraint的對應方法
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

//MASViewConstraint.m中對應的方法,MASCompositeConstraint其實也類似,只是迴圈調用每一個子約束的該方法
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
    return ^id(id attribute, NSLayoutRelation relation) {
        //如果傳入的參數是一個數組 則逐個約束解析後以組形式添加約束
        if ([attribute isKindOfClass:NSArray.class]) {
            NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");
            NSMutableArray *children = NSMutableArray.new;
            for (id attr in attribute) {
                MASViewConstraint *viewConstraint = [self copy];
                viewConstraint.layoutRelation = relation;
                viewConstraint.secondViewAttribute = attr;
                [children addObject:viewConstraint];
            }
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            compositeConstraint.delegate = self.delegate;
            [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        } else {
            //單一約束 則直接賦值
            NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
            self.layoutRelation = relation;
            self.secondViewAttribute = attribute;
            return self;
        }
    };
}

3 Masonry框架的整體運用的理解 

進過上面的原理分析,大體理解了其調用和實現眼裡,接下來,我們通過下麵的這句代碼在容器中的演變過程來進行整體感受一下,下麵的演變過程來自:Masonry源碼學習,原文有幾處有點小問題修改過,大家參考的時候註意甄別和判斷。

make.top.right.bottom.left.equalTo(superview)
  • make.top
    • 生成對象A:MASViewConstraint(view.top)
    • 將A的delegate設為make
    • 將A放入make的constraints中,此時make.constraints = [A]
    • 返回A
  • make.top.right
    • 生成對象B:MASViewConstraint(view.right)
    • 使用A和B生成MASCompositeConstraint對象C,將C的delegate設為make
    • 將make.constraints中替換成C,此時make.constraints = [C],C.childConstraints = [A,B]
    • 返回C
  • make.top.right.bottom
    • 生成對象D:MASViewConstraint(view.bottom),將D的delegate為C
    • 將D放入C.childConstraints中,此時C.childConstraints = [A,B,D]
    • 返回C
  • make.top.right.bottom.left
    • 生成對象E:MASViewConstraint(view.left),E的delegate為C
    • 將E放入C.childConstraints中,此時C.childConstraints = [A,B,D,E]
    • 返回C
  • make.top.right.bottom.left.equalTo(superview)
    • 會依次調用A,B,D,E的equalTo(superView)

在上面的過程中可以看到:

  • 對make.constraints的添加和替換元素的操作
  • 對MASCompositeConstraint對象的添加元素的操作(當.equalTo(@[view1,view2])時就有替換操作了,在裡面沒體現出)。
  • 每個constraint的delegate為它的父容器,因為需要父容器來執行添加和替換約束的操作。

4 Masonry框架的整體架構

  盜用iOS開發之Masonry框架源碼解析中的一張圖,這張圖將Masonry框架的架構闡述的很清晰,Masonry框架主要分為4個部分:

  • View+MASAdditions:最左邊的紅色框的這個類,這是Masonry框架最主要的一個類,主要是最下麵的四個添加和修改約束的方法
  • MASConstraintMaker:中間綠色框中的這個類,這是Masonry框架中的過渡類,鏈接最左邊和最右邊之間的關係,也是鏈式語法的發起點和添加約束的執行點。MASConstraintMaker類就是一個工廠類,負責創建和安裝MASConstraint類型的對象(依賴於MASConstraint介面,而不依賴於具體實現)。
  • 核心類:最右邊的黃色框的這個類群,這是Masonry框架中的核心基礎類群,這個類群又分為兩個部分:
    • 約束類群:黃色框上面三個類,其中MASConstraint是一個抽象類,不可被實例化。我們可以將MASConstraint是對NSLayoutConstriant的封裝,看做是一個介面或者協議。MASViewConstraint和MASCompositeConstraint都繼承自MASConstraint,其中MASViewConstraint用於定義一個單獨的約束,而MASCompositeConstraint則用於定義一組約束條件,例如定義size、insert等參數時返回的其實都是MASCompositeConstraint。
    • 屬性類群:主要是指MASViewAttribute,主要是對NSLayoutAttribute的擴展,方便我們進行約束定義和修改
  • 附屬類群:還有一些工具類沒有在這張圖中進行展示,例如NSArray+MASAdditions、NSLayoutConstraint+MASDebugAdditions、MASLayoutConstraint等,都定義了一些工具和簡化方法。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 因為手頭沒有安卓手機,無法調試模塊的AP模式,於是我使用了筆記本電腦——路由器——無線模塊的STA模式調試。 其中只測試了三種情況:TCP伺服器。TCP客戶端。UDP(我認為是UDP客戶端)。 1.需要修改MDK5的源代碼SSID和password (1)模塊做TCP伺服器 1.模塊的IP地址已經在 ...
  • 在編寫腳本的時候經常會遇到這種情況,某些程式的命令執行的之後可能會要求用戶進行輸入,這個時候就需要一些特殊寫法來應對這種問題了。這裡參考 "這篇文章" 提到可以使用 delimiter 分界符來解決。 也就是說在 Shell 將 delimiter 分界符之後的的所有內容都會當做輸入,直到遇到第二個 ...
  • Nagios Check_hp HP 2014年11月18日 下午 08:49 https://IP:2381 [root@nagios ~]# tar zxvf check_hp_bladechassis-1.0.1.tar.gz [root@nagios check_hp_bladechassi ...
  • 註意:修改完(visudo)之後,需要visudo -c檢查一下是否有錯誤。 sudo -l查看用戶擁有的許可權。 1、sudoers配置文件別名介紹 (1)Host Aliases(主機別名) 生產環境中一般不會設置主機別名,一般主機別名不太常用 配置服務 創建日誌保存目錄 [root@localh ...
  • 今天閑下來,試試曾經成功安裝過的Google-chrome的安裝 先加上幾個參考的鏈接: 一個是更改yum配置文件直接用yum install的,沒看他貼出安裝成功的照片,自己也試過,不行 http://www.linuxidc.com/Linux/2016-05/131538.htm 去掉vim ...
  • 位置是從1 開始 substr("ABCDEFG", 0); //返回:ABCDEFG,截取所有字元 substr("ABCDEFG", 1); //返回:ABCDEFG,截取所有字元 substr("ABCDEFG", -0); //返回:ABCDEFG,截取所有字元 -0=0,都是從左邊截 su ...
  • 一,assign 代表設置時候直接賦值,而不是複製或者保留它。 二,retain. 會在賦值的時候把新值保留。此屬性只能用於Object-C對象類型。 三,copy 在賦值時,將新值複製一份,複製工作由copy執行,此屬性只對那些實行了NSCopying協議的對象類型有效。 參考資料:《iPhone ...
  • 簡介 在開發過程中,經常會使用到第三框架,我們通過一個pod install命令,很方便的就將第三方框架加到我們自己的項目中。 如果我們也想將自己寫的組件或庫開源出去,讓別人也可以通過pod install命令安裝自己的框架該怎麼做呢?下麵,我就教大家一步一步的將自己的pods發佈到CocoaPod ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...