Masonry的入门学习

news2024/9/21 16:51:32

Masonry的学习

文章目录

  • Masonry的学习
    • 前言
    • 使用Masonry
      • Masonry支持的属性
      • 修饰语
      • 基础API
      • Auto Boxing
      • 中心点
      • 设置边距
      • 优先级
      • 创建约束
      • 更新约束
      • 使用Masonry来布局UIScrollview
    • 小结

前言

在日常的开发中,我们如果面对一些很复杂的UI布局,我们如果统一使用frame的方式来设置的话,会相当复杂,这时候就孕育而生了自动布局,但是在我们原生的环境下,苹果原生的自动布局语言比较复杂,而我们现在要学习的一个Masonry这个第三方库会给我们提供了更加简洁的语法。

使用Masonry

Masonry支持的属性

Masonry支持下类别的属性

@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;

属性可以大致分成以下几个部分的内容:

  • 尺寸:width、height、size。
  • 边界:left、leading、right、trailing、top、bottom、edges。
  • 中心点:center、centerX、centerY。
  • 偏移量:offset、insets、sizeOffset、centerOffset。
  • priority()约束优先级(0~1000),multipler乘因数,dividedBy除因数。

这里给出一张图片便于理解这些相关属性的内容。

在这里插入图片描述

修饰语

Masonry为了让代码使用和阅读更容易理解,Masonry还添加了andwith两个方法,这两个方法内部只是将self返回,没有任何实际作用,仅仅只是为了方便阅读。

make.top.and.bottom.equalTo(self.containerView).with.offset(padding);

这些修饰符的返回。

- (MASConstraint *)with {
    return self;
}

基础API

下面列出了这里常用的一些基础的API

mas_makeConstraints()    //添加约束
mas_remakeConstraints()  //移除之前的约束,重新添加新的约束
mas_updateConstraints()  //更新约束
equalTo()       //参数是对象类型,一般是视图对象或者 mas_width 这样的坐标系对象
mas_equalTo()   //和上面功能相同,参数可以传递基础数据类型对象,可以理解为比上面的API更强大
width()         //用来表示宽度,例如代表view的宽度
mas_width()     //用来获取宽度的值。和上面的区别在于,一个代表某个坐标系对象,一个用来获取坐标系对象的值

Auto Boxing

在上面可以看到我们上面有一些API的功能其实类似,只不过有前缀mas_之分,在实际开发中我们需要注意一下这两个API的差异,但是我们也可以通过两个宏定义来让我们不用区分这两种API的区别。

// 定义这个常量,就可以不用在开发过程中使用mas_前缀。
#define MAS_SHORTHAND
// 定义这个常量,就可以让Masonry帮我们自动把基础数据类型的数据,自动装箱为对象类型。
#define MAS_SHORTHAND_GLOBALS

我们来看一下下面这段代码来解释一下这个Auto Boxing的一个简单过程

UIView* view = [[UIView alloc] init];// 创建一个view
view.backgroundColor = UIColor.redColor;
[self.view addSubview:view];//	添加到父亲视图
UIEdgeInsets pddings = UIEdgeInsetsMake(20, 20, 20, 20);
[view makeConstraints:^(MASConstraintMaker *make) {// 添加Masonry约束 注意一定要在我们添加到父亲视图之后进行一个添加
  // 设置宽度和高度为100
  // euqalTo(@100),这里不用mas前缀的话,参数要加@.
  // 如果我们想要直接使用数字的话,Masonry提供了mas_前缀。
  // 这个前缀会自动将基础数据类型转换为NSNumber类型。
  // 这个过程叫做封箱(Auto Boxing)。
  // "mas_xx"开头的宏定义,内部都是通过MASBoxValue()函数实现的。
  // 这样的宏定义主要有四个,分别是mas_equalTo()、mas_offset()和大于等于、小于等于四个。
  // 不过因为引用了开头两个宏定义。所以这里应该不加也没事。
  make.height.width.equalTo(100);
}];

这部分代码与注释来源于iOS开发——Masonry的使用。

中心点

[view makeConstraints:^(MASConstraintMaker *make) {// 添加Masonry约束 注意一定要在我们添加到父亲视图之后进行一个添加
  make.center.equalTo(self.view);//我们可以通过这种方式将这个视图添加到我们的视图中心点的位置。
}];

设置边距

UIEdgeInsets pddings = UIEdgeInsetsMake(20, 20, 20, 20);
[view makeConstraints:^(MASConstraintMaker *make) {// 添加Masonry约束 注意一定要在我们添加到父亲视图之后进行一个添加
  //这里需要注意如果设置过宽度和高度,那么就意味着下面可以生效的仅仅之后左侧边距和上方的边距可以生效。
  make.top.equalTo(self.view.mas_top).with.offset(pddings.top);
	make.left.equalTo(self.view.mas_left).with.offset(pddings.left);
  //注意如果是数值的方式来设置的话,我们记得将右下的数值改成负值。
  make.right.equalTo(self.view.mas_right).with.offset(-pddings.right);
  make.bottom.equalTo(self.view.mas_bottom).with.offset(-pddings.bottom);
  
  //上面代码等同于下面这一行的代码
  make.edges.equalTo(self.view).insets(pddings);
}];

优先级

  • priority可以指定一个确切的优先级值,范围在0 ~ 1000,值越大优先级越高。
  • priorityHigh相当于UILayoutPriorityDefaultHigh,优先级值为750。
  • priorityMedium介于高优先级和低优先级之间,优先级值在250 ~ 750之间。
  • priorityLow相当于UILayoutPriorityDefaultLow,优先级值为250。

优先级写在约束链的最后方

make.top.equalTo(self.view).with.priority(800);
make.bottom.equalTo(self.view).priorityMedium();
make.left.equalTo(self.view).priorityLow();
make.right.equalTo(self.view).priorityHigh();

创建约束

自动布局允许将宽度和长度设置为一个固定值,例如:

[view makeConstraints:^(MASConstraintMaker *make) {
        make.height.width.equalTo(100);

    }];

但是,自动布局不允许将对齐属性(如leftrightcenterY等)设置为常量值。如果为这些属性传递了一个NSNumber类型的值,Masonry会将这些变为相对于视图的父视图的约束,即:

[self.greenView makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(@10);
}];

=>上面的代码会被转化成下方的代码

[self.greenView makeConstraints:^(MASConstraintMaker *make) {
	make.left.equalTo(self.view.mas_left).offset(10);
}];

更新约束

[控件 mas_remakeConstraints:^(MASConstraintMaker *make) {
//这个方法会将以前的约束全部删除,添加新的约束
}];
[控件 mas_updateConstraints:^(MASConstraintMaker *make) {
//这个方法将会覆盖以前的某些特定的约束
}];

我们这里采用第二个方式来进行一个更新,这里笔者给出一个比较简单的案例

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view1 = [[UIView alloc] init];
    UIView* vc = [[UIView alloc] init];
    vc.backgroundColor = UIColor.greenColor;
    _view1.backgroundColor = UIColor.redColor;
    [self.view addSubview:_view1];
    UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
    [_view1 makeConstraints:^(MASConstraintMaker *make) {
        //make.height.width.equalTo(100);
        //make.center.equalTo(self.view);
        make.edges.equalTo(self.view).insets(pddings);
    }];
    UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(press:)];
    tap.numberOfTapsRequired = 1;
    tap.numberOfTouchesRequired = 1;
    [_view1 addGestureRecognizer:tap];
   
    
    [_view1 addSubview:vc];
    
    [vc makeConstraints:^(MASConstraintMaker *make) {
        make.height.width.equalTo(40);
        
    }];

    
    // Do any additional setup after loading the view.
}
-(void)press:(UIGestureRecognizer*)tap {
    if (flag) {
        UIEdgeInsets pddings = UIEdgeInsetsMake(10, 10, 10, 10);
        [_view1 updateConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.view).insets(pddings);
        }];
        flag = NO;
    } else {
        UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
        [_view1 updateConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.view).insets(pddings);
        }];
        flag = YES;
    }
    
}

下面是一个实现的效果。

在这里插入图片描述

这里简单总结一下有关约束的问题:

  • mas_makeConstraints只负责新增约束,每次调用会创建新的约束,如果对已存在的约束再次创建,会提示冲突(此时会存在对同一属性的两条约束)。
  • mas_updateConstraints用于更新之前创建的约束,如果对已存在的约束进行更新,没有任何问题(此时只存在对同一属性的最新一条约束)。
  • mas_remakeConstraints会清除之前的所有约束,仅保留最新的约束。

使用Masonry来布局UIScrollview

对UIScrollView使用自动布局的时候,经常会遇到很多问题,比如布局无效,不能滑动等。主要是因为UIScrollView除了有自己的frame之外,还有一个contentSize。如果对子视图进行布局时参考了scrollView,例如设置子视图的leading/trailing/top/bottom属性等于scrollView的leading/trailing/top/bottom,这实际上是子视图相对于scrollview的contentsize来确定的,而非bounds。而且由于scrollview的contentSize又是根据子视图的位置决定的,这样就形成了一个依赖循环。iOS自动布局内容选自这里

所以我们该怎么使用Masonry来布局UIScrollview,这里我们最重要的内容就是把我们内部的视图布局与UIScrollView的关系剥离开,这里我们就可以使用一个过渡的视图,将过渡视图和原先的UIScrollView重叠,然后对于UIScrollVIew内部的视图全部都布局在这个过渡视图中间。

  • 子视图不能依赖任何scrollView有关的布局,即不能参考scrollView的位置。
  • 子视图除了要确定自己的大小以外,还需要确定自己与contentSize四周的距离,以此来确定contentSize。

这里我们开始通过仿写一下照片墙居中的一个小demo来尝试使用MAsonry来布局一下UIscrollView,下面先给出代码。

我们先确定我们UIScrollView的布局

[scrollView makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0));//布局UIScrollView
    }];

然后设置一个过渡视图,来确定UIScrollView的ContentSize的大小

[_contentView makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(scrollView);//确定ContentSize的大小
      
        make.width.equalTo(scrollView);//竖向滑动固定宽度,横向滑动固定高度
    }];

然后我们就可以把所有的原本需要添加到UIScrollView的视图全部添加到现在的过渡视图中

viewInsert.backgroundColor = UIColor.redColor;
        viewInsert.tag = 100 + i;
        //UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
        [viewInsert makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(i / 3 * 140 + 70);
            make.left.equalTo(i % 3 * 120 + 20);
            make.width.equalTo(100);
            make.height.equalTo(100);
        }];

最后我们还需要通过最后一个视图来确定我们过渡视图的底部的位置

[_contentView makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(previousView);
    }];

最后这是完整的代码

- (void)viewDidLoad {
    [super viewDidLoad];
    //self.view1 = [[UIView alloc] init];
    UIScrollView* scrollView = [[UIScrollView alloc] init];
    scrollView.backgroundColor = UIColor.orangeColor;
    [self.view addSubview: scrollView];
    [scrollView makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0));//布局UIScrollView
    }];
    self.contentView = [[UIView alloc] init];
    _contentView.backgroundColor = UIColor.clearColor;
    [scrollView addSubview:_contentView];
    [_contentView makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(scrollView);//确定ContentSize的大小
      
        make.width.equalTo(scrollView);//竖向滑动固定宽度,横向滑动固定高度
    }];
    UIButton* previousView = [UIButton buttonWithType:UIButtonTypeCustom];
    for (int i = 0; i < 9; i++) {
        UIButton* viewInsert = [UIButton buttonWithType:UIButtonTypeCustom];
        NSString* str = [NSString stringWithFormat:@"%d.jpg", i + 1];
        UIImage* image = [UIImage imageNamed:str];
        [viewInsert setImage:image forState:UIControlStateNormal];
        [viewInsert setImage:image forState:UIControlStateSelected];
        if (flag == YES) {
            NSLog(@"11");
            [viewInsert addTarget:self action:@selector(press:) forControlEvents:UIControlEventTouchUpInside];
        }
        //UIView* viewInsert = [[UIView alloc] init];
        [self.contentView addSubview:viewInsert];
        viewInsert.backgroundColor = UIColor.redColor;
        viewInsert.tag = 100 + i;
        //UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
        [viewInsert makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(i / 3 * 140 + 70);
            make.left.equalTo(i % 3 * 120 + 20);
            make.width.equalTo(100);
            make.height.equalTo(100);
        }];
        previousView = viewInsert;
    }
    [_contentView makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(previousView);
    }];

-(void)press:(UIButton*)btn {
    
    if (flag && !btn.selected) {
        
        btn.selected = YES;
        [self.contentView bringSubviewToFront:btn];
        
        [btn remakeConstraints:^(MASConstraintMaker *make) {
            make.center.equalTo(self.view);
            make.height.and.width.equalTo(300);
        }];
        flag = NO;
    } else if (!flag && btn.selected) {
        NSLog(@"22");
       
        btn.selected = NO;
        NSInteger i = (btn.tag - 100) / 3;
        NSLog(@"%ld", i);
        NSInteger j = (btn.tag - 100) % 3;
        [btn remakeConstraints:^(MASConstraintMaker *make) {
            make.height.and.width.equalTo(100);
            make.top.equalTo(i * 140 + 70);
            make.left.equalTo(j  * 120 + 20);
        }];
        flag = YES;
    } else {
        ;
    }
    
}


@end

最后实现效果:

在这里插入图片描述

小结

  • 对视图使用自动布局之前,需要先将视图添加到父视图上。

  • 使用Masonry时注意区分

    mas_equalToequalTo的区别:

    mas_equalTo

    • 是对equalTo的封装,支持NSNumber、CGSize、CGPoint、UIEdgeInsets;
    • mas_equalTo是一个Macro,比较的是值;

    • 一般对于数值元素使用mas_equalTo,例如make.width.mas_equalTo(100);

    • equalTo

      • 只支持基本类型;
      • equalTo比较的是view;
      • 对于对象或多个属性的处理使用equalTo,例如make.left.and.right.equalTo(self.view);
  • 对视图使用自动布局后,如果尝试获取它的frame很可能获取到的是0。对于这种情况,我们可以在获取视图frame之前手动调用layoutIfNeeded方法来更新布局。

  • Masonry中的block不存在循环引用的问题,不必使用weakSelf。虽然block内引用了view,但block并没有被view所持有,因此不会发生循环引用。

笔者认为这段总结很好,摘自iOS开发—Masonry

这是笔者第一次接触MAsonry这个第三方库以及相关的一些使用,之后会继续学习相关内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2094354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发编程之AtomicUnsafe魔法类详解

并发编程之Atomic&Unsafe魔法类详解_并发魔法类-CSDN博客

京东2025校招/社招内推信息

社招链接&#xff1a;https://zhaopin.jd.com/web/job/job_info_list/3 社招内推码&#xff1a;JC8DI 校招链接&#xff1a;https://campus.jd.com/home#/ 校招内推码&#xff1a;C49D1 有需要的同学也可私信我姓名、电话、邮箱内推已经投递的同学可私信我获取最新进展哦

国内访问GitHub很卡,steam连接断开怎么办

目录 第一章、问题分析1.1&#xff09;问题1.2&#xff09;解决&#xff1a;下载个加速器就好了 友情提醒: 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置。 第一章、问题分析 1.1&#xff09;问题 国内访问GitHub很卡怎…

Docker容器技术详解

一、Docker简介及部署方法 1.1Docker简介 1.1.1什么是docker Docker是管理容器的引擎&#xff0c;为应用打包、部署平台&#xff0c;而非单纯的虚拟化技术 docker的重要特点和优势&#xff1a; 1. 轻量级虚拟化Docker 容器相较于传统的虚拟机更加轻量和高效&#xff0c;能够…

基于langchain的多轮对话RAG

目录 概述 整体架构 代码实现 输出展示 参考 概述 相比于单轮对话&#xff0c;多轮对话要考虑历史对话记录&#xff0c;大模型需要根据对话上下文去回答用户的问题。在RAG的场景中&#xff0c;通常需要通过问题去召回和问题相关的知识&#xff0c;再将知识和问题交给大模型…

江大白 | 大模型时代,CV目标检测任务,会走向何方?

本文来源公众号“江大白”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;大模型时代&#xff0c;CV目标检测任务&#xff0c;会走向何方&#xff1f; 以下文章来源于知乎&#xff1a;深度眸 作者&#xff1a;深度眸 编辑&#…

华为手机永久关闭自动亮度 | 使用智慧场景

问题 使用华为手机时&#xff0c;无法在设置中永久性关闭自动亮度&#xff0c;因为每次手机重启后都会自动打开自动亮度。此问题目前无法通过设置去解决&#xff0c;但可以通过华为的智慧场景解决&#xff0c;下文介绍解决方案。 解决方案 智慧场景 打开智慧生活APP&#xf…

JavaScript 作用链

JavaScript 作用链是指在 JavaScript 中查找变量时所遵循的规则和路径。它描述了当前执行上下文及其父级上下文之间的关系。作用链用于解析变量&#xff0c;当在一个作用域中查找变量时&#xff0c;如果当前作用域中没有找到&#xff0c;则会沿着作用链向上查找&#xff0c;直到…

华为OD机试真题 - 查找充电设备组合 - 子集和问题(Java/Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试真题(Java/Python/JS/C/C++)》。 刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX…

51单片机——LED点阵屏

1、点阵屏简介 LED点阵屏由若干个独立的LED组成&#xff0c;LED以矩阵的形式排列&#xff0c;以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合&#xff0c;如汽车报站器、广告屏以及公告牌等 LED点阵屏分类 按颜色&#xff1a;单色、双色、全彩 …

多目标应用:基于SPEA2的移动机器人路径规划研究(提供MATLAB代码)

一、机器人路径规划介绍 移动机器人&#xff08;Mobile robot&#xff0c;MR&#xff09;的路径规划是 移动机器人研究的重要分支之&#xff0c;是对其进行控制的基础。根据环境信息的已知程度不同&#xff0c;路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或…

【网络安全】命令执行漏洞—远程代码执行及远程系统命令

命令执行漏洞是指Web应用程序中由于对用户输入的验证不足&#xff0c;导致恶意用户能够构造并传递一些预期之外的命令给服务器&#xff0c;从而在服务器上执行任意命令。这种漏洞可能允许攻击者获取敏感信息、破坏服务、访问系统文件&#xff0c;甚至获得服务器的控制权。 一、…

minio文件存储+ckplayer视频播放(minio分片上传合并视频播放)

文章目录 参考简述效果启动minio代码配置类RedisConfigWebConfigMinioClientAutoConfigurationOSSPropertiesapplication.yml 实体类MinioObjectResultStatusCodeOssFileOssPolicy 工具类FileTypeUtilMd5UtilMediaTypeMinioTemplate 文件分片上传与合并MinioFileControllerMini…

JVM堆内存空间(heap)

在Java程序运行时&#xff0c;系统运行过程中产生的大部分实例对象以及数组对象都会被放到堆中存储。 默认情况下&#xff0c;如果不通过参数强制指定堆空间大小&#xff0c;那么JVM会根据当前所在的平台进行自适应调整&#xff0c;起始大小默认为当前物理机器内存的1/64&…

我的第一个QT程序_创建项目_全程图解

创建项目 1.打开QT开发工具 2.点【创建项目】 在弹出的窗口的项目列表中选择【Application(Qt)】中间列选择 第一项 如上图。 3.输入项目名称和项目保存的路径 4.Build system下拉列表中有三个选项&#xff0c;虽然Qt6主推CMake&#xff0c;所以可以默认&#xff0c;直接下一步…

【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(二)

看到这句话的时候证明&#xff1a;此刻你我都在努力 加油陌生人 个人主页&#xff1a;Gu Gu Study 专栏&#xff1a;用Java学习数据结构系列 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努力留下足迹 喜欢的话可以点个赞谢谢了。 作者&#xff…

基于yolov8的玉米病害检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的玉米病害检测系统是一款利用前沿深度学习技术开发的智能农业工具。该系统以YOLOv8为核心算法&#xff0c;通过大量玉米病害图片的训练&#xff0c;能够精准识别玉米害虫病害。 该系统具备高效、准确的检测能力&#xff0c;支持图片、批量图片、视频…

7.整数反转

7.整数反转 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 1&#x…

Linux主机网络参数的设置—IP地址的作用和类型

网络参数管理 一.网络参数 主机名&#xff0c;IP地址&#xff0c;子网掩码&#xff0c;网关&#xff0c;DNS服务器地址 1.配置主机名 hostname命令来查看当前系统的主机名&#xff0c; hosnamectl set-hostname 修改centos7的主机名&#xff0c; 建议以FQDN的&#xff…

顶级 USB 恢复工具探讨:2024 -2025 年最佳 USB 数据恢复

在数字数据与物理文档一样重要的时代&#xff0c;丢失 USB 驱动器中的数据可能是一种令人恐慌的经历。无论是重要的工作文件、珍贵的照片还是重要文档&#xff0c;数据丢失都会以难以想象的方式让您倒退。值得庆幸的是&#xff0c;一些 USB 恢复工具旨在帮助您度过这些可怕的时…