-
Masonry框架github地址:
https://github.com/SnapKit/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);
}];
}
运行的效果:
于是乎,通过这个框架,我们在给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状态下:
Selected状态下:
怎么样,是不是很爽,有时需要给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
对象,至此,我们大概可以认为已经确定了要给view
的top
设置约束,并且返回值是一个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)
这行代码执行就可以让 yellowView
的top
距self.view
的top
为0
了(默认是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()
(button
的frame
和label
缩进)),我也写了很多注释,相信能帮到你们,然后对demo
有什么疑问的地方,或者有什么好的建议,希望大家联系我,共同探讨
写到这里,我也要结束装逼了,大家一起装逼,才是真的装逼
button
demo的github地址:https://github.com/2098187liujing/-demo
ending