-
Masonry框架github地址:
- 首先我們來導入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);
}];
}
運行的效果:
于是乎,通過這個框架,我們在給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)下:
Selected狀態(tài)下:
怎么樣,是不是很爽,有時需要給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)確定了要給view
的top
設置約束,并且返回值是一個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í)行就可以讓 yellowView
的top
距self.view
的top
為0
了(默認是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()
(button
的frame
和label
縮進)),我也寫了很多注釋,相信能幫到你們,然后對demo
有什么疑問的地方,或者有什么好的建議,希望大家聯(lián)系我,共同探討
寫到這里,我也要結束裝逼了,大家一起裝逼,才是真的裝逼
button
demo的github地址:https://github.com/2098187liujing/-demo
ending