iOS页面适配的正确姿势
2019-12-03

我这里讲解使用的是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是在视图类中就可以使用,更加方便了我们去做适配。通过参考安全区域或者之前的顶部布局参考,我们不在需要去判断机型,也能达到页面完美适配的目的。

 

, 1, 0, 9);