iOS第三方 - Masonry
Masonry
1 - 手写代码所经历的关于页面布局的三个时期
① 在 iPhone - iPhone3gs 时代,window 的 size 固定为 320*480,只需要简单计算一下相对位置就好了
② 在 iPhone4 - iPhone4s 时代,苹果推出了 retina 屏,但是给了码农们非常大的福利:window 的 size 不变
③ 在 iPhone5 - iPhone5s 时代,window 的 size 变成 320*568,这时 autoresizingMask 派上了用场(为啥这时候不用 Autolayout ?因为还要支持 iOS 5 )
④ 在 iPhone6 + 时代,window 的 width 也发生了变化(相对 5/5s 的屏幕比例没有变化) 终于是时候抛弃 autoresizingMask 改用 autolayout 了
⑤ 如何快速的上手 autolayout 呢? 当年 iOS 6 推出的同时新增了 autolayout 的特性 , 但使用起来过于的繁琐和啰嗦,直到 iPhone 6 发布之后,使用 autolayout 就势在必行。此时第三方库 Masonry 就排上了用场:没有学习任何的官方文档或者其它的关于 autolayout 的知识,便可自由进行页面布局
2 - 使用 iOS 原生的框架代码来进行 autoLayout 布局,无论是代码量还是结构的清晰度,表现力度难免差强人意,而在 storyBoard 中只需要几条线就可以搞定的事情,用代码却要写冗余的一大堆,并且有的时候,storyBoard 并不能解决所有问题
3 - Masonry 是一个轻量级布局框架,拥有自身的描述性语法,它采用优雅的链式语法对自动布局进行了封装,同时支持 iOS 和 Mac OS XMasonry。Masonry 中的一系列属性与 NSLayoutAttribute 的对应关系如下
leading 与 left、trailing、right 在正常情况下是等价的,但是当一些布局是从右至左时(比如阿拉伯文) 则会对调(基本可以不理不用),只管用 left 和 right 即可
4 - 三个方法让你玩转 Masonry
① 添加约束:创建一个 label,将其尺寸设置为 50*50,显示在屏幕中间(在添加约束前,受约束的视图必须添加到父视图上)
1 #import "Masonry.h"// 头文件 2 #import "ViewController.h" 3 @implementation ViewController 4 5 - (void)viewDidLoad { 6 [super viewDidLoad]; 7 self.view.backgroundColor = [UIColor whiteColor]; 8 9 // 受约束视图 10 UILabel *label = [[UILabel alloc]init]; 11 label.backgroundColor = [UIColor redColor]; 12 // 必须有父视图 13 [self.view addSubview:label]; 14 // 这个方法用于在最开始时为控件设置约束 15 [label mas_makeConstraints:^(MASConstraintMaker *make) { 16 make.center.equalTo(self.view); 17 make.height.equalTo(@50);// equalTo 仅支持 NSNumber | CGPoint | CGSize | UIEdgeInsets 数据类型 18 make.width.equalTo(@50); // 等同于 make.size.mas_equalTo(CGSizeMake(50,50)); 19 }]; 20 } 21 22 @end
equalTo 和 mas_equalTo的区别 ? 其实 mas_equalTo 是一个 MACRO,可以看到 mas_equalTo 只是对其参数进行了一个 BOX 操作(装箱)
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__)))
② 更新约束:当需要配合布局改变或者动画效果的时候,可能需要将已经添加的约束进行更新,更新约束的作用在于更新某些添加的约束,并不会移除掉原有的约束
1 #define MAS_SHORTHAND 2 // 该宏可在 masonry 进行约束时,省略 mas_ 的使用, 3 // 注意不要定义到 masonry 头文件的下方 4 #define MAS_SHORTHAND_GLOBALS 5 #import "Masonry.h" 6 #import "ViewController.h" 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 self.view.backgroundColor = [UIColor whiteColor]; 12 13 UILabel * label = [[UILabel alloc]init]; 14 label.backgroundColor = [UIColor redColor]; 15 [self.view addSubview:label];// 16 17 // 起始尺寸 50 * 50 18 [label mas_makeConstraints:^(MASConstraintMaker *make) { 19 make.center.equalTo(self.view); 20 make.height.equalTo(@50); 21 make.width.equalTo(@50); 22 }]; 23 24 25 // 更新布局:mas_updateConstraints 26 [label updateConstraints:^(MASConstraintMaker *make) { 27 28 make.height.equalTo(300);// equalTo 此时相当于 mas_equalTo。因宏的原因,mas_equalTo 支持的数据类型也更加广泛 29 make.width.equalTo(@100); 30 }]; 31 } 32 33 34 @end
③ 重设约束:它会清除之前所有的约束,只保留重设的约束
1 #define MAS_SHORTHAND 2 #define MAS_SHORTHAND_GLOBALS 3 #import "Masonry.h" 4 #import "ViewController.h" 5 @implementation ViewController 6 7 - (void)viewDidLoad { 8 [super viewDidLoad]; 9 self.view.backgroundColor = [UIColor whiteColor]; 10 11 UILabel * label = [[UILabel alloc]init]; 12 label.backgroundColor = [UIColor redColor]; 13 [self.view addSubview:label];// 14 15 // 起始约束 16 [label mas_makeConstraints:^(MASConstraintMaker *make) { 17 make.center.equalTo(self.view); 18 make.height.equalTo(@50); 19 make.width.equalTo(@50); 20 }]; 21 22 // 重设约束 23 [label remakeConstraints:^(MASConstraintMaker *make) { 24 25 make.left.equalTo(self.view.mas_left).offset(10); 26 make.top.equalTo(self.view).offset(100);// 此时 self.view 默认为 self.view.top 27 make.height.equalTo(@200); 28 make.width.equalTo(@350); 29 }]; 30 } 31 32 33 @end
运行效果:起始位置 | 重设约束
③ 边距
1 #define MAS_SHORTHAND 2 #define MAS_SHORTHAND_GLOBALS 3 #import "Masonry.h" 4 #import "ViewController.h" 5 @implementation ViewController 6 7 - (void)viewDidLoad { 8 [super viewDidLoad]; 9 self.view.backgroundColor = [UIColor whiteColor]; 10 self.navigationController.navigationBar.hidden = YES; 11 12 UILabel * label = [[UILabel alloc]init]; 13 label.backgroundColor = [UIColor redColor]; 14 [self.view addSubview:label]; 15 16 // 起始约束 50*50 17 [label mas_makeConstraints:^(MASConstraintMaker *make) { 18 make.center.equalTo(self.view); 19 make.height.equalTo(@50); 20 make.width.equalTo(@50); 21 }]; 22 23 24 // 边距 25 [label remakeConstraints:^(MASConstraintMaker *make) { 26 27 // // 方式一:边距均 30 个点 28 // make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(30, 30, 30, 30)); 29 30 31 // // 方式二:具体设置 32 // make.top.equalTo(self.view).with.offset(30); 33 // make.left.equalTo(self.view).with.offset(30); 34 // // 计算的 bottom 需要小于父视图的底部高度,所以这里使用负数 -30 35 // make.bottom.equalTo(self.view).with.offset(-30); 36 // make.right.equalTo(self.view).with.offset(-30); 37 38 // 方式三:约束要和 UIEdgeInsetsMake 参数表达相一致:上左下右 39 make.top.left.bottom.and.right.equalTo(self.view).with.insets(UIEdgeInsetsMake(30, 30, 30, 30)); 40 }]; 41 } 42 43 @end
运行效果:起始位置 | 边距
这里有意思的地方是 and 和 with 其实这两个函数什么事情都没做,但是用在这种链式语法中就非常的巧妙和易懂
1 - (MASConstraint *)with { 2 return self; 3 } 4 5 - (MASConstraint *)and { 6 return self; 7 }
5 - 其他示例
① 让两个高度为 150 的 UI 垂直居中且等宽、等间隔排列,间隔为 10
1 #define MAS_SHORTHAND 2 #define MAS_SHORTHAND_GLOBALS 3 #import "Masonry.h" 4 #import "ViewController.h" 5 @implementation ViewController 6 7 - (void)viewDidLoad { 8 [super viewDidLoad]; 9 self.view.backgroundColor = [UIColor whiteColor]; 10 11 UILabel *label1 = [[UILabel alloc] init]; 12 label1.backgroundColor = [UIColor redColor]; 13 [self.view addSubview:label1]; 14 15 UILabel *label2 = [[UILabel alloc] init]; 16 label2.backgroundColor = [UIColor grayColor]; 17 [self.view addSubview:label2]; 18 19 20 [label1 makeConstraints:^(MASConstraintMaker *make) { 21 22 make.height.equalTo(150); 23 make.centerY.equalTo(self.view.centerY); 24 make.left.equalTo(self.view.left).offset(10); 25 make.right.equalTo(label2.left).offset(-10); 26 // 关键点 27 make.width.equalTo(label2.width); 28 29 }]; 30 31 32 [label2 makeConstraints:^(MASConstraintMaker *make) { 33 34 make.height.equalTo(label1.height); 35 make.centerY.equalTo(label1.centerY); 36 make.right.equalTo(self.view.right).offset(-10); 37 make.left.equalTo(label1.right).offset(10); 38 // 关键点 关键点 关键点 重要的事情说三遍! 39 make.width.equalTo(label1.width); 40 41 }]; 42 } 43 44 @end
运行效果:在两个 UI 之间互相设置的约束,可以看到它们的宽度在约束下可自动被计算出来
② 在 UIScrollView 中顺序排列一些 view 并自动计算 contentSize
1 #import "Masonry.h" 2 #import "ViewController.h" 3 @implementation ViewController 4 5 - (void)viewDidLoad { 6 [super viewDidLoad]; 7 self.view.backgroundColor = [UIColor whiteColor]; 8 self.navigationController.navigationBar.hidden = YES; 9 10 // UIScrollView 11 UIScrollView *scrollView = [UIScrollView new]; 12 [self.view addSubview:scrollView]; 13 [scrollView mas_makeConstraints:^(MASConstraintMaker *make) { 14 15 make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(20,5,5,5)); 16 }]; 17 18 // UIView 19 UIView *container = [UIView new]; 20 [scrollView addSubview:container]; 21 [container mas_makeConstraints:^(MASConstraintMaker *make) { 22 23 make.edges.equalTo(scrollView);// 视图滚动关键点 ①:边距设置后,UIScrollView 的 contentSize 自行填充 24 make.width.equalTo(scrollView); 25 }]; 26 27 28 29 // 约定遍历 UIView 个数 30 int count = 8; 31 UIView *lastView = nil; 32 for ( int i = 1 ; i <= count ; i++ ){ 33 34 UIView *subv = [UIView new]; 35 [container addSubview:subv]; 36 subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 ) 37 saturation:( arc4random() % 128 / 256.0 ) + 0.5 38 brightness:( arc4random() % 128 / 256.0 ) + 0.5 39 alpha:1]; 40 41 // 约束 42 [subv mas_makeConstraints:^(MASConstraintMaker *make) { 43 44 make.left.and.right.equalTo(container); 45 make.height.mas_equalTo(20*i); 46 47 if (lastView){ 48 make.top.mas_equalTo(lastView.mas_bottom); 49 }else{ 50 make.top.mas_equalTo(container.mas_top); 51 } 52 }]; 53 54 lastView = subv; 55 } 56 57 58 [container mas_makeConstraints:^(MASConstraintMaker *make) { 59 // 视图滚动关键点 ②:移除此约束后,UIScrollView 将不会滚动 60 make.bottom.equalTo(lastView.mas_bottom); 61 }]; 62 } 63 64 @end
运行效果:关键就在于 container 这个 view 起到了一个中间层的作用,它能够自动计算 UIScrollView 的 contentSize
链接:Masonry
https://pan.baidu.com/s/1PLfA5W3F73Gjt-RS_agXjg
qwf5
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)