Coding源码学习第四部分(Masonry介绍与使用(一))

Masonry GitHub:https://github.com/SnapKit/Masonry

Masonry是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了并具有高可读性,而且同时支持 iOS 和 Mac OS X。Masonry是一个用代码写iOS或OS界面的库,可以代替Auto layout。

example :

1 [view1 mas_makeConstraints:^(MASConstraintMaker *make) {    
2 
3     // 设置和superView 的边缘的缩进量
4     make.edges.equalTo(superview).with.insets(padding);
5 
6 
7 }];

Masonry使用讲解

Masonry 支持的属性:

make 的属性:(MASConstraintMaker:)

 1 @interface MASConstraintMaker : NSObject
 2 
 3 /**
 4  *    The following properties return a new MASViewConstraint
 5  *  with the first item set to the makers associated view and the appropriate MASViewAttribute
 6  */
 7 @property (nonatomic, strong, readonly) MASConstraint *left;
 8 @property (nonatomic, strong, readonly) MASConstraint *top;
 9 @property (nonatomic, strong, readonly) MASConstraint *right;
10 @property (nonatomic, strong, readonly) MASConstraint *bottom;
11 @property (nonatomic, strong, readonly) MASConstraint *leading;
12 @property (nonatomic, strong, readonly) MASConstraint *trailing;
13 @property (nonatomic, strong, readonly) MASConstraint *width;
14 @property (nonatomic, strong, readonly) MASConstraint *height;
15 @property (nonatomic, strong, readonly) MASConstraint *centerX;
16 @property (nonatomic, strong, readonly) MASConstraint *centerY;
17 @property (nonatomic, strong, readonly) MASConstraint *baseline;

其中leading与left trailing与right 在正常情况下是等价的 但是当一些布局是从右至左时, 则会对调 换句话说就是基本可以不理不用 用left和right就好了

需要添约束的View 的约束的属性:

 1 @interface MAS_VIEW (MASAdditions)
 2 
 3 /**
 4  *    following properties return a new MASViewAttribute with current view and appropriate NSLayoutAttribute
 5  */
 6 @property (nonatomic, strong, readonly) MASViewAttribute *mas_left;
 7 @property (nonatomic, strong, readonly) MASViewAttribute *mas_top;
 8 @property (nonatomic, strong, readonly) MASViewAttribute *mas_right;
 9 @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottom;
10 @property (nonatomic, strong, readonly) MASViewAttribute *mas_leading;
11 @property (nonatomic, strong, readonly) MASViewAttribute *mas_trailing;
12 @property (nonatomic, strong, readonly) MASViewAttribute *mas_width;
13 @property (nonatomic, strong, readonly) MASViewAttribute *mas_height;
14 @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerX;
15 @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerY;
16 @property (nonatomic, strong, readonly) MASViewAttribute *mas_baseline;
17 @property (nonatomic, strong, readonly) MASViewAttribute *(^mas_attribute)(NSLayoutAttribute attr);

Masonry 提供了3个关于约束的方法:

1 - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block; // mas_makeConstraints 只负责新增约束 Autolayout不能同时存在两条针对于同一对象的约束 否则会报错 
2 - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block; // mas_updateConstraints 针对上面的情况 会更新在block中出现的约束 不会导致出现两个相同约束的情况
3 - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block; // mas_remakeConstraints 则会清除之前的所有约束 仅保留最新的约束
4 三种函数善加利用 就可以应对各种情况了

mas_makeConstraints 是给view添加约束,约束有几种,分别是边距,宽,高,左上右下距离,基准线。

添加过约束后可以有修正,修正有offset(位移)修正和multipliedBy(倍率)修正。

语法一般是 make.equalTo or make.greaterThanOrEqualTo or make.lessThanOrEqualTo + 倍数和位移修正。

注意点1:

使用 mas_makeConstraints方法的元素必须事先添加到父元素的中,例如[self.view addSubview:view];

注意点2:

mas_equalTo 和 equalTo 区别:mas_equalTo 比equalTo多了类型转换操作,一般来说,大多数时候两个方法都是 通用的,但是对于数值元素使用mas_equalTo。对于对象或是多个属性的处理,使用equalTo。特别是多个属性时,必须使用equalTo.

举例 make.left.and.right.equalTo(self.view) ,  make.height.mas_equalTo(@20);

1 #define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
2 #define mas_greaterThanOrEqualTo(...)    greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
3 #define mas_lessThanOrEqualTo(...)       lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
4 
5 #define mas_offset(...)                  valueOffset(MASBoxValue((__VA_ARGS__)))

mas_equalTo 是一个宏,可以看到 mas_equalTo只是对其参数进行了一个BOX操作(装箱) MASBoxValue的定义具体可以看看源代码

所支持的类型 除了NSNumber支持的那些数值类型之外,就只支持CGPoint CGSize UIEdgeInsets

mas_greaterThanOrEqualTo 和 mas_lessThanOrEqualTo 这两个宏还有一个小技巧:

1 #ifdef MAS_SHORTHAND_GLOBALS
2 
3 #define equalTo(...)                     mas_equalTo(__VA_ARGS__)
4 #define greaterThanOrEqualTo(...)        mas_greaterThanOrEqualTo(__VA_ARGS__)
5 #define lessThanOrEqualTo(...)           mas_lessThanOrEqualTo(__VA_ARGS__)
6 
7 #define offset(...)                      mas_offset(__VA_ARGS__)
8 
9 #endif

定义以下两个宏,在使用Masonry框架时就不需要加mas_前缀了
(定义宏一定要在引入Masonry.h文件之前).

 1 //define this constant if you want to use Masonry without the 'mas_' prefix
 2 #define MAS_SHORTHAND
 3 //define this constant if you want to enable auto-boxing for default syntax
 4 #define MAS_SHORTHAND_GLOBALS
 5 
 6 // example
 7 
 8 
 9 #define MAS_SHORTHAND
10 #define MAS_SHORTHAND_GLOBALS
11 #import "Masonry.h"

注意点3: 注意到方法with和and,这连个方法其实没有做任何操作,方法只是返回对象本身,这这个方法的左右完全是为了方法写的时候的可读性 。

make.left.and.right.equalTo(self.view);和make.left.right.equalTo(self.view);是完全一样的,但是明显的加了and方法的语句可读性 更好点。

1 #pragma mark - Semantic properties
2 
3 - (MASConstraint *)with {
4     return self;
5 }
6 
7 - (MASConstraint *)and {
8     return self;
9 }

小技巧:#define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;  快速的定义一个weakSelf ,用于block 里面,防止循环引用。

示例:

(1) 居中显示一个View :

 1     WS(weakSelf); // WS 是上面定义的一个宏
 2     // 从此基本可以抛弃CGRectMake 了
 3     UIView *sv = [UIView new];
 4     sv.backgroundColor = [UIColor blackColor];
 5     // 在做autoLayout 之前,一定要将view 添加到superView 上,否则会报错
 6     [self.view addSubview:sv];
 7     // mas_makeConstraints 就是Masonry 的autoLayout 添加函数,将所需的约束添加到block 中就行了
 8     [sv mas_makeConstraints:^(MASConstraintMaker *make) {
 9         // 将sv 居中(很容易理解)
10         make.center.equalTo(weakSelf.view);
11         // 将size 设置为(400 ,400)
12         make.size.mas_equalTo(CGSizeMake(400, 400));
13     }];

(2)让一个View 略小于其superView(边距是10)

 1     // 让一个View 略小于其superView(边框为 10)
 2     UIView *sv1 = [UIView new];
 3     sv1.backgroundColor = [UIColor redColor];
 4     [sv addSubview:sv1];
 5     [sv1 mas_makeConstraints:^(MASConstraintMaker *make) {
 6         make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
 7         // 等价于
 8 //        make.top.equalTo(sv).with.offset(10);
 9 //        make.left.equalTo(sv).with.offset(10);
10 //        make.bottom.equalTo(sv).with.offset(-10);
11 //        make.right.equalTo(sv).with.offset(-10);
12         // 也等价于
13 //        make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
14     }];

可以看到 edges 其实就是top,left,bottom,right的一个简化 分开写也可以 一句话更省事

那么为什么bottom和right里的offset是负数呢? 因为这里计算的是绝对的数值 计算的bottom需要小于sv的底部高度 所以要-10 同理用于right

(3)让两个高度为150 的View 垂直居中且等宽且等间隔排列 间隔是10 (自动计算其宽度)

 1     // 初级 让两个高度为150 的View 垂直居中且等宽且等间隔排列 间隔为10 (自动计算其宽度)
 2     int padding1 = 10;
 3     UIView *sv2 = [UIView new];
 4     sv2.backgroundColor = [UIColor yellowColor];
 5     [sv addSubview:sv2];
 6     UIView *sv3 = [UIView new];
 7     sv3.backgroundColor = [UIColor yellowColor];
 8     [sv addSubview:sv3];
 9     
10     [sv2 mas_makeConstraints:^(MASConstraintMaker *make) {
11         make.centerY.mas_equalTo(sv.mas_centerY);
12         make.left.equalTo(sv.mas_left).with.offset(padding1);
13         make.right.equalTo(sv3.mas_left).with.offset(-padding1);
14         make.height.mas_equalTo(@150);
15         make.width.equalTo(sv3);
16     }];
17 
18     [sv3 mas_makeConstraints:^(MASConstraintMaker *make) {
19         make.centerY.mas_equalTo(sv.mas_centerY);
20         make.left.equalTo(sv2.mas_right).with.offset(padding1);
21         make.right.equalTo(sv.mas_right).with.offset(-padding1);
22         make.height.mas_equalTo(@150);
23         make.width.equalTo(sv2);
24     }];

这里我们在两个子view之间互相设置的约束 可以看到他们的宽度在约束下自动的被计算出来了

(4)在UIScrollerView 顺序排列一些View 并自动计算contentSize

 1     // 中级 在UIScrollerView 顺序排列一些View 并自动计算contentSize
 2     UIScrollView *scrollView = [UIScrollView new];
 3     scrollView.backgroundColor = [UIColor whiteColor];
 4     [sv addSubview:scrollView];
 5     [scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
 6         make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(5, 5, 5, 5)); // 约束UIScrollerView 的窗口视图的大小四边各缩进sv 5
 7     }];
 8     UIView *container = [UIView new]; // 起一个中间层的作用,能够自动计算UIScrollerView 的contentSize
 9     [scrollView addSubview:container];
10     [container mas_makeConstraints:^(MASConstraintMaker *make) {
11         make.edges.equalTo(scrollView); // left right top bottom 
12         make.width.equalTo(scrollView);
13     }];
14     
15     int count = 10;
16     UIView *lastView = nil;
17     for (int i = 1; i <= count; i++) {
18         UIView *subV = [UIView new];
19         [container addSubview:subV];
20         subV.backgroundColor = [UIColor colorWithHue:(arc4random() % 256 / 256.0) saturation:(arc4random() % 128 / 256.0) brightness:(arc4random() % 128 / 256.0) alpha:1]; // 设置背景颜色
21         [subV mas_makeConstraints:^(MASConstraintMaker *make) {
22             make.left.and.right.equalTo(container);
23             make.height.mas_equalTo(@(20 * i)); // 每个子View 的高度不断增大一倍
24             if (lastView) {
25                 make.top.mas_equalTo(lastView.mas_bottom); // lastView 做中间View 的传递,确定每个小View 的top
26             } else {
27                 make.top.mas_equalTo(container.mas_top); // 第一个小View 的top 和container 相等
28             }
29         }];
30         lastView = subV;
31     }
32     
33     [container mas_makeConstraints:^(MASConstraintMaker *make) {
34         make.bottom.equalTo(lastView.mas_bottom); // container 的bottom 和最后一个小View 的bottom 相等
35     }];

(5)横向或者纵向等间隙的排列一组View 

autoLayout并没有直接提供等间隙排列的方法(Masonry的官方demo中也没有对应的案例) 但是参考案例3 我们可以通过一个小技巧来实现这个目的 为此写一个Category

 1 #import "UIView+Masonry_LJC.h"
 2 #import "Masonry.h"
 3 
 4 @implementation UIView (Masonry_LJC)
 5 
 6 - (void)distributeSpacingHorizontallyWith:(NSArray *)views {
 7     NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count + 1];
 8     
 9     for (int i = 0; i < views.count + 1; ++i) {
10         UIView *v = [UIView new];
11         [spaces addObject:v];
12         [self addSubview:v];
13         
14         [v mas_makeConstraints:^(MASConstraintMaker *make) {
15             make.width.equalTo(v.mas_height); // 宽高相等
16         }];
17     }
18     
19     UIView *v0 = spaces[0];
20     
21     __weak __typeof (&*self) ws = self;
22     
23     [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
24         make.left.equalTo(ws.mas_left);
25         make.centerY.equalTo(((UIView *)views[0]).mas_centerY);
26     }];
27     
28     UIView *lastSpace = v0;
29     for (int i = 0; i < views.count; ++i) {
30         UIView *obj = views[i];
31         UIView *space = spaces[i + 1];
32         
33         [obj mas_makeConstraints:^(MASConstraintMaker *make) {
34             make.left.equalTo(lastSpace.mas_right);
35         }];
36         
37         [space mas_makeConstraints:^(MASConstraintMaker *make) {
38             make.left.equalTo(obj.mas_right);
39             make.centerY.equalTo(obj.mas_centerY);
40             make.width.equalTo(v0);
41         }];
42         
43         lastSpace = space;
44     }
45     
46     [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
47         make.right.equalTo(ws.mas_right);
48     }];
49 }
50 
51 - (void)distributeSpacingVerticallyWith:(NSArray *)views {
52     NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count + 1];
53     
54     for (int i = 0; i < views.count + 1; ++i) {
55         UIView *v = [UIView new];
56         [spaces addObject:v];
57         [self addSubview:v];
58         
59         [v mas_makeConstraints:^(MASConstraintMaker *make) {
60             make.width.equalTo(v.mas_height);
61         }];
62     }
63     
64     UIView *v0 = spaces[0];
65     
66     __weak __typeof (&*self) ws = self;
67     
68     [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
69         make.top.equalTo(ws.mas_top);
70         make.centerX.equalTo(((UIView *)views[0]).mas_centerX);
71     }];
72     
73     UIView *lastSpace = v0;
74     for (int i = 0; i < views.count; ++i) {
75         UIView *obj = views[i];
76         UIView *space = spaces[i + 1];
77         
78         [obj mas_makeConstraints:^(MASConstraintMaker *make) {
79             make.top.equalTo(lastSpace.mas_bottom);
80         }];
81         
82         [space mas_makeConstraints:^(MASConstraintMaker *make) {
83             make.top.equalTo(obj.mas_bottom);
84             make.centerX.equalTo(obj.mas_centerX);
85             make.height.equalTo(v0);
86         }];
87         
88         lastSpace = space;
89     }
90     
91     [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
92         make.bottom.equalTo(ws.mas_bottom);
93     }];
94 }
95 @end

测试:

 1     UIView *sv11 = [UIView new];
 2     UIView *sv12 = [UIView new];
 3     UIView *sv13 = [UIView new];
 4     UIView *sv21 = [UIView new];
 5     UIView *sv31 = [UIView new];
 6     sv11.backgroundColor = [UIColor redColor];
 7     sv12.backgroundColor = [UIColor redColor];
 8     sv13.backgroundColor = [UIColor redColor];
 9     sv21.backgroundColor = [UIColor redColor];
10     sv31.backgroundColor = [UIColor redColor];
11     [sv addSubview:sv11];
12     [sv addSubview:sv12];
13     [sv addSubview:sv13];
14     [sv addSubview:sv21];
15     [sv addSubview:sv31];
16     //给予不同的大小 测试效果
17     [sv11 mas_makeConstraints:^(MASConstraintMaker *make) {
18         make.centerY.equalTo(@[sv12,sv13]);
19         make.centerX.equalTo(@[sv21,sv31]);
20         make.size.mas_equalTo(CGSizeMake(40, 40));
21     }];
22     [sv12 mas_makeConstraints:^(MASConstraintMaker *make) {
23         make.size.mas_equalTo(CGSizeMake(70, 20));
24     }];
25     [sv13 mas_makeConstraints:^(MASConstraintMaker *make) {
26         make.size.mas_equalTo(CGSizeMake(50, 50));
27     }];
28     [sv21 mas_makeConstraints:^(MASConstraintMaker *make) {
29         make.size.mas_equalTo(CGSizeMake(50, 20));
30     }];
31     [sv31 mas_makeConstraints:^(MASConstraintMaker *make) {
32         make.size.mas_equalTo(CGSizeMake(40, 60));
33     }];
34     [sv distributeSpacingHorizontallyWith:@[sv11,sv12,sv13]];
35     [sv distributeSpacingVerticallyWith:@[sv11,sv21,sv31]];

可以看下这个较复杂页面的约束,其中的桃红色的线条都是约束线: 

本篇先到此,下篇做更深入的学习。

 

posted @ 2016-08-04 21:01  鳄鱼不怕牙医不怕  阅读(598)  评论(0编辑  收藏  举报