我这里讲解使用的是 Masonry, 我假设你对约束有一定的了解.
随着 iPhone X 的出现, iOS 页面的适配似乎也麻烦了起来, 我见得最多的就是通过某种手段判断机型或者获取导航栏的高度, 然后计算宽高. 我不说这种方法好不好, 因为它也能解决你目前的问题, 但不是我喜欢的方式.
在正式开始之前, 我先介绍几个重要的知识:
1.topLayoutGuide 和 bottomLayoutGuide
这两个属性属于 UIViewController,topLayoutGuide 主要就是指导航栏, 状态栏; bottomLayoutGuide 主要指 TabBar(刘海手机上也可指代底部黑条的部分), 主要就是为了让你使用约束的时候对顶部和底部有个参考, 避免视图上的内容被遮挡.
2.safeAreaLayoutGuide
这个属性是 iOS11 才有的, 也就是苹果对于刘海屏给出的一种解决方案. 它和 1 中的两个属性作用类似, 由于属于 UIView 类, 不受限于 UIViewController, 所以在灵活性上更强.
从它的名字可以看出来, 它是一种安全区域的参考, 那么安全区域指哪块呢? 如下图:
从上面的图可以看得出来, 安全区域可以保证我们的内容不被遮挡.
3. Masonry 中相应的属性
既然我们用 Masonry, 我们就需要知道与系统中对应的属性是哪些:
- // 安全区域对应的属性
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuide NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeading NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTrailing NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeft NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideRight NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTop NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideBottom NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideWidth NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideHeight NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideCenterX NS_AVAILABLE_IOS(11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideCenterY NS_AVAILABLE_IOS(11.0);
- // 一般参考对应的属性
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuide NS_DEPRECATED_IOS(8.0, 11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuide NS_DEPRECATED_IOS(8.0, 11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideTop NS_DEPRECATED_IOS(8.0, 11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideBottom NS_DEPRECATED_IOS(8.0, 11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideTop NS_DEPRECATED_IOS(8.0, 11.0);
- @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideBottom NS_DEPRECATED_IOS(8.0, 11.0);
从名字中我们就能很清晰的识别出来
4. 实战
我们创建一个简单的工程, 初始页面是一个带有导航栏的红色视图控制器, 如下图所示:
我们将在这上面创建一个绿色的视图, 来具体看一下上面 1 和 2 提到的属性怎么去用.
首先我们不使用上面的属性来设置约束, 代码如下:
- UIView *view = [[UIView alloc] init];
- view.backgroundColor = [UIColor greenColor];
- [self.view addSubview:view];
- [view mas_makeConstraints:^(MASConstraintMaker *make) {
- make.top.equalTo(self.view.mas_top);
- make.height.equalTo(@200);
- make.left.right.equalTo(self.view);
- }];
我们只是简单的设置了子视图和父视图之间的约束, 似乎看起来没什么问题, 但是当我们运行一下就会发现, 不好的事情发生了.
我们的绿色视图竟然被导航栏遮住了一部分, 这不是我们所希望了, 因为将来有可能遮住我们的重要信息, 也许你想着我们可以修改约束中 make.top.equalTo(self.view.mas_top) 为 make.top.equalTo(self.view.mas_top).offset(88), 让其偏移 88, 但是这样的坏处是显而易见的, 现在偏移 88 是没问题的, 但运行在没有刘海的手机上又要改为偏移 64, 假如苹果将来又出个神奇的手机, 你是不是又要去判断手机型号, 然后偏移某一个值呢? 从现在起, 放弃这种适配方法吧 (除了个别目的).
我们把代码修改为下面这个样子
- UIView *view = [[UIView alloc] init];
- view.backgroundColor = [UIColor greenColor];
- [self.view addSubview:view];
- [view mas_makeConstraints:^(MASConstraintMaker *make) {
- if (@available(iOS 11, *)) {
- make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
- } else {
- make.top.equalTo(self.mas_topLayoutGuide);
- }
- make.height.equalTo(@200);
- make.left.right.equalTo(self.view);
- }];
这个代码运行出来的效果如下, 这样的效果就是我们想要的, 内容不会被导航栏遮挡. 注意上面的代码, 有一个 if 语句, 我们判断了相应 API 能否在指定平台获取, 而这也是我们适配的关键, 因为 iPhone X 以及之后出来的手机, 系统肯定是在 iOS11 之上的 (除了个别越狱的), 所以我们适配刘海屏的思路就是判断系统版本就够了.
我们再来看看底部的适配, 先上代码
- UIView *view = [[UIView alloc] init];
- view.backgroundColor = [UIColor greenColor];
- [self.view addSubview:view];
- [view mas_makeConstraints:^(MASConstraintMaker *make) {
- make.height.equalTo(@200);
- make.left.right.equalTo(self.view);
- make.bottom.equalTo(self.view.mas_bottom);
- }];
这种运行出来的效果为:
也许你认为这样没什么不好, 你说得对, 这样是没什么不好, 但是底部大约有 32 的距离是系统不希望我们使用的, 因为怕和系统的手势冲突. 所以这里我们也是要用到底部的参考和安全区域的参考
- UIView *view = [[UIView alloc] init];
- view.backgroundColor = [UIColor greenColor];
- [self.view addSubview:view];
- [view mas_makeConstraints:^(MASConstraintMaker *make) {
- make.height.equalTo(@200);
- make.left.right.equalTo(self.view);
- make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom); // 或者 make.bottom.equalTo(self.mas_bottomLayoutGuide);
- }];
运行出来的效果如下, 我们可以看到底部空出来了一段距离, 这段距离就是系统不希望我们使用的.
4. 总结
通过上面的例子, 我们可以看到 topLayoutGuide 和 bottomLayoutGuide 与 safeAreaLayoutGuide 的作用区别不大, 但是 safeAreaLayoutGuide 是在视图类中就可以使用, 更加方便了我们去做适配. 通过参考安全区域或者之前的顶部布局参考, 我们不在需要去判断机型, 也能达到页面完美适配的目的.
来源: https://www.cnblogs.com/Devhwl/p/9908394.html