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 {
    // 創(chuàng)建按鈕
    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) {
        
        // 設置普通狀態(tài)的圖片 背景圖片 文字顏色 文字內(nèi)容
        make.normal.imgName(@"LJbtn_img_normal").bgImgName(@"LJbtn_bgImg_normal").color([UIColor redColor]).title(@"我是普通狀態(tài)");
        
        // 設置選中狀態(tài)的圖片 背景圖片 文字顏色 文字內(nèi)容
        make.selected.imgName(@"LJbtn_img_selectedl").bgImgName(@"LJbtn_bgImg_selected").color([UIColor blueColor]).title(@"我是選中狀態(tài)");
    }];
    // 添加
    [self.view addSubview:button];
}

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

我們來看下運行之后的效果:
Normal狀態(tài)下:

Snip20160707_2.png

Selected狀態(tài)下:

Snip20160707_3.png

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

那這個是怎么實現(xiàn)的呢?

  • 首先我們來看一下masonry怎么實現(xiàn)的:
    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 無返回值 參數(shù)為 約束管理者對象的 block
 *
 *  @return 存有所有約束的數(shù)組
 */
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    // 將view自帶的約束設置為NO 避免沖突
    self.translatesAutoresizingMaskIntoConstraints = NO;
    // 創(chuàng)建約束管理者 并將 self 傳進去   此時的self是 當前方法的調(diào)用者
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    // 調(diào)用傳進來的的block
    block(constraintMaker);
    // 返回存有所有約束行為的數(shù)組
    return [constraintMaker install];
}
  • 這個類是UIView的一個分類

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

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

    • 那現(xiàn)在我們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 {
    
    // 執(zhí)行添加約束的方法
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

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

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

發(fā)現(xiàn)這個方法最重執(zhí)行的是[self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]這個方法,繼續(xù)com + 左鍵進去,我們來到這個方法的實現(xiàn):

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    // 創(chuàng)建一個約束
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    // 創(chuàng)建一個具體的約束的管理者 并把約束傳過去
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    // 因為傳過來的nil 所以不執(zhí)行
    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 執(zhí)行此方法
    if (!constraint) {
        // 設置代理
        newConstraint.delegate = self;
        // 將約束添加到存有約束的數(shù)組
        [self.constraints addObject:newConstraint];
    }
    // 返回具體的約束管理者
    return newConstraint;
}

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

  • 我們接著看這行代碼make.top.equalTo(self.view).offset(100);,make.top是確定了給view的哪個位置設置約束,我們在來看看make.top.equalTo(self.view)這行代碼,還是一樣,com + 左鍵equalTo(self.view)里面:
/**
 *  返回值為: 返回值為 MASConstraint對象 參數(shù)為 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)這行代碼執(zhí)行就可以讓 yellowViewtopself.viewtop0了(默認是0)

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

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

  • 最后我們總結一下

    • 為什么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);
    

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

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

    block作為參數(shù)的時候,這個block是由外部來實現(xiàn),內(nèi)部調(diào)用的

    block作為返回值的時候,這個block是由內(nèi)部來實現(xiàn),外部調(diào)用的

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

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內(nèi)容

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