Masonry框架淺析-鏈式編程

  • 首先我們來導入Masonry看下效果:
#import "ViewController.h"
#import "Masonry.h"



@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 添加一個黃色的view
    [self addYellowView];
    
}



- (void)addYellowView {
    
    UIView *yellowView = [[UIView alloc]init];
    
    yellowView.backgroundColor = [UIColor yellowColor];
    
    [self.view addSubview:yellowView];
    
    // 設置約束
    [yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        // 設置頂部的約束 距self.view頂部為100
        make.top.equalTo(self.view).offset(100);
        
        // 設置左邊的約束
        make.left.equalTo(self.view).offset(20);
        
        // 設置右邊的約束
        make.right.equalTo(self.view).offset(-20);
        
        // 設置高
        make.height.equalTo(@50);
        
    }];

}

運行的效果:


Snip20160707_1.png

于是乎,通過這個框架,我們在給UIButton設置一些屬性的時候可以這樣做:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 添加一個按鈕
    [self addButton];
    
}

- (void)addButton {
    // 創建按鈕
    UIButton *button = [[UIButton alloc]init];
    
    // 設置frame
    button.frame = CGRectMake(50, 100, 150, 50);
    
    // 綁定了點擊事件
    [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
    
    // 設置屬性
    [button lj_makeAttribute:^(LJButtonManager *make) {
        
        // 設置普通狀態的圖片 背景圖片 文字顏色 文字內容
        make.normal.imgName(@"LJbtn_img_normal").bgImgName(@"LJbtn_bgImg_normal").color([UIColor redColor]).title(@"我是普通狀態");
        
        // 設置選中狀態的圖片 背景圖片 文字顏色 文字內容
        make.selected.imgName(@"LJbtn_img_selectedl").bgImgName(@"LJbtn_bgImg_selected").color([UIColor blueColor]).title(@"我是選中狀態");
    }];
    // 添加
    [self.view addSubview:button];
}

- (void)buttonAction:(UIButton *)sender {
    // 切換按鈕狀態
    sender.selected = !sender.selected;
    
}

我們來看下運行之后的效果:
Normal狀態下:

Snip20160707_2.png

Selected狀態下:

Snip20160707_3.png

怎么樣,是不是很爽,有時需要給button多個不同狀態設置屬性,可以這樣點 點 點(.image.bgImage.color.title.frame) 想點什么,自己就往里面加什么方法, 是不是很爽

那這個是怎么實現的呢?

  • 首先我們來看一下masonry怎么實現的:
    UIView *yellowView = [[UIView alloc]init];
    
    yellowView.backgroundColor = [UIColor yellowColor];
    
    [self.view addSubview:yellowView];
    
    // 設置約束
    [yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        // 設置頂部的約束 距self.view頂部為100
        make.top.equalTo(self.view).offset(100);
        
        // 設置左邊的約束
        make.left.equalTo(self.view).offset(20);
        
        // 設置右邊的約束
        make.right.equalTo(self.view).offset(-20);
        
        // 設置高
        make.height.equalTo(@50);
        
    }];

我們com + 左鍵mas_makeConstraints: 到這個方法里面去看一下

// UIView 的分類
@implementation MAS_VIEW (MASAdditions)

/**
 *  添加約束的方法
 *
 *  @param block 無返回值 參數為 約束管理者對象的 block
 *
 *  @return 存有所有約束的數組
 */
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    // 將view自帶的約束設置為NO 避免沖突
    self.translatesAutoresizingMaskIntoConstraints = NO;
    // 創建約束管理者 并將 self 傳進去   此時的self是 當前方法的調用者
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    // 調用傳進來的的block
    block(constraintMaker);
    // 返回存有所有約束行為的數組
    return [constraintMaker install];
}
  • 這個類是UIView的一個分類

    • 我們的view在調用mas_makeConstraints:這個方法的時候,需要傳遞一個 無返回值,參數為MASConstraintMaker對象的block
      我們在調用這個方法的時候,需要傳入這樣子的一個block,并且給這個block賦值,賦值的過程就相當于我們在給view設置約束
  • 這個是怎么設置約束的呢

    • make.top.equalTo(self.view).offset(100);我們來看下這行代碼,這行代碼里面,這行代碼里面,是通過make這個對象設置約束的,make.top表示給頂部添加約束

    • 那現在我們com + 左鍵到這個MASConstraintMaker里面看下:

@interface MASConstraintMaker : NSObject

/**
 *  The following properties return a new MASViewConstraint
 *  with the first item set to the makers associated view and the appropriate MASViewAttribute
 */
@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;

這些就是我們需要view的那些位置設置約束,但是我們可以看到@property (nonatomic, strong, readonly) MASConstraint *top; 這個top位置屬性是一個MASConstraint的對象,來到MASConstraintMaker.m文件

我們找到了這個top屬性的get方法:

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    
    // 執行添加約束的方法
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

- (MASConstraint *)top {
    // 給view添加一個頂部位置的約束 返回值為方法調用者本身 這樣又可以接著調用方法
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}

發現這個方法最重執行的是[self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]這個方法,繼續com + 左鍵進去,我們來到這個方法的實現:

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    // 創建一個約束
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    // 創建一個具體的約束的管理者 并把約束傳過去
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    // 因為傳過來的nil 所以不執行
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
    // 傳過來的為nil 執行此方法
    if (!constraint) {
        // 設置代理
        newConstraint.delegate = self;
        // 將約束添加到存有約束的數組
        [self.constraints addObject:newConstraint];
    }
    // 返回具體的約束管理者
    return newConstraint;
}

我們可以看到這里面就是在進行設置約束的一些操作 ,最后的返回值是MASViewConstraint對象,至此,我們大概可以認為已經確定了要給viewtop設置約束,并且返回值是一個MASViewConstraint對象

  • 我們接著看這行代碼make.top.equalTo(self.view).offset(100);,make.top是確定了給view的哪個位置設置約束,我們在來看看make.top.equalTo(self.view)這行代碼,還是一樣,com + 左鍵equalTo(self.view)里面:
/**
 *  返回值為: 返回值為 MASConstraint對象 參數為 id類型 的一個block
 */
- (MASConstraint * (^)(id))equalTo {
    
    // 返回一個block
    return ^id(id attribute) {
        
        // 給 view 的top 設置相對于 attribute 設置約束
        // 此時的 attribute 就是我們 make.top.equalTo(self.view).offset(100); 中的self.view
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

到這里,我們基本上就可以認為make.top.equalTo(self.view)這行代碼執行就可以讓 yellowViewtopself.viewtop0了(默認是0)

  • 接著,我們繼續看make.top.equalTo(self.view).offset(100);,還是一樣com + 左鍵offset(100):
/**
 *  返回值為: 返回值是 MASConstraint對象 ,參數是 CGFloat類型的 一個block
 */
- (MASConstraint * (^)(CGFloat))offset {
    // 返回block
    return ^id(CGFloat offset){
        // 設置偏移值
        self.offset = offset;
        // block的返回值  返回自己
        return self;
    };
}

到這里,make.top.equalTo(self.view).offset(100);這行代碼就執行完了,這個里面還有很多步驟,由于Masonry的作者封裝的比較狠,理解起來困難還是大大的, 我這里也只是簡單的介紹了一下這行代碼的執行思路

  • 最后我們總結一下

    • 為什么make.top.equalTo(self.view).offset(100); 可以這樣子一直點點點 ;make.top相當于get方法,這個方法的返回值的對象本身- (MASConstraint *)top

    我們也可以寫成這樣:

            MASConstraint *make1 = make.top;
          
          MASConstraint *make2 = make1.equalTo(self.view);
          
          MASConstraint *make3 = make2.offset(100);
    

    接著每次調用get方法之后,我又可以拿到調用者本身,于是我們又可以接著調方法,就可以一直點點點了;
    然后就是make.top.equalTo(self.view).offset(100);這個括號里面的參數,.offset的返回值是一個MASConstraint * (^)(CGFloat)block; 我們在執行了make.top.equalTo(self.view).offset之后,就可以拿到這個block;可以寫成這樣:

            // 定義block
          MASConstraint *(^block)(CGFloat);
          
          // 拿到返回回來的block
          block = make.top.equalTo(self.view).offset;
          
          // 調用block
          block(100);
          
          // 拿到block的返回值
          MASConstraint *make = block(100);
    

    block作為參數的時候,這個block是由外部來實現,內部調用的

    block作為返回值的時候,這個block是由內部來實現,外部調用的

可能大家看到這里還是很多不明白,大家可以下一下我的demo,demo寫的非常簡單,你們下載之后,根據自己的理解,可以自己添加方法(比如.frame().titleEdgeInsets()(buttonframelabel縮進)),我也寫了很多注釋,相信能幫到你們,然后對demo有什么疑問的地方,或者有什么好的建議,希望大家聯系我,共同探討
寫到這里,我也要結束裝逼了,大家一起裝逼,才是真的裝逼
buttondemo的github地址:https://github.com/2098187liujing/-demo
ending

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • iOS_autoLayout_Masonry 概述 Masonry是一個輕量級的布局框架與更好的包裝AutoLay...
    指尖的跳動閱讀 1,195評論 1 4
  • (一)Masonry介紹 Masonry是一個輕量級的布局框架 擁有自己的描述語法 采用更優雅的鏈式語法封裝自動布...
    木易林1閱讀 2,392評論 0 3
  • Masonry是一個輕量級的布局框架,擁有自己的描述語法,采用更優雅的鏈式語法封裝自動布局,簡潔明了并具有高可讀性...
    3dcc6cf93bb5閱讀 1,828評論 0 1
  • 下面主要介紹Kotlin在聲明常量與變量這一塊的變化,其完整的聲明格式模板為: val 或 varval聲明常量,...
    我想吃碗牛肉面閱讀 290評論 0 0
  • 如果說我有第二次生命,真的是因為他。 在遭遇了被李可劈腿被甩然后我還樂呵呵的去找他,用最后那點卑微得可憐的自尊心去...
    貓耳朵yami閱讀 195評論 0 0