我这里讲解使用的是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);
从名字中我们就能很清晰的识别出来
self项目实践——底部safeArea适配
static CGFloat const kbottomButtonViewHeight = 55;
/// 底部按钮
- (void)initBottomButtonView
{
[self.view addSubview:self.bottomSafeAreaBgView];
[self.bottomSafeAreaBgView addSubview:self.bottomButtonView];
[self.bottomSafeAreaBgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.bottomButtonView).offset(-10);
make.leading.trailing.mas_equalTo(self.view);
make.bottom.mas_equalTo(self.view);
}];
[self.bottomButtonView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.mas_equalTo(self.bottomSafeAreaBgView);
make.height.mas_equalTo(kbottomButtonViewHeight);
if (@available(iOS 11.0, *)) {
make.bottom.mas_equalTo(self.view.mas_safeAreaLayoutGuideBottom);
//make.bottom.mas_equalTo(self.view.mas_safeAreaLayoutGuideBottom).offset(10);
} else {
make.bottom.mas_equalTo(self.view);
}
}];
}
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是在视图类中就可以使用,更加方便了我们去做适配。通过参考安全区域或者之前的顶部布局参考,我们不在需要去判断机型,也能达到页面完美适配的目的。
网友评论