1.为什么会有这个问题?
因为iphoneX为了实现全面屏,iPhone X之前的手机 状态栏高度(20.f) + 导航栏高度(44.f) = 64.f,iPhone X是 状态栏高度(44.f) + 导航栏高度(44.f) = 88.f.
差别在于状态栏差了24.因为现在是刘海屏幕,中间的部分被摄像头等部件等硬件挡住就显示不全了.
还有一个区别是底部是34的小黑条,小黑条的作用是替代被取消的Home键的作用, 原来手机如果之前是有home键,可以按住Home键从当前应用回到桌面,现在按键被取消了,可以通过往上滑动小黑条从当前应用回到桌面.
总结一下,对于一般的应用有导航栏和tabBar的情况来说,内容一般都是从导航栏的下面开始显示,到tabbar上面,这一般是应用的显示范围.但是对于上下滑动的比方说scrollView,tableView,collectionView来说,我们显示在整个屏幕,这时就可以提现全面屏的更大显示空间.还有对于webview,如果底部没有需要点击的按钮,或者tabar,我们都是可以将内容显示到屏幕的最下面.
核心是顶部是遮挡的内容你是否在意,下面是否有点击操作和下面的小黑条的操作冲突.如果你在意上面遮挡的部分,就要从刘海下面开始布局UI,如果你的底部有按钮点击的操作,按钮的底部要距离屏幕在34以上,以防止误操作,影响用户体验.
下面是两个案例:
适配前:
Snip20220415_16.png
适配后:
Snip20220415_17.png
适配前:
Snip20220415_19.png
适配后:
Snip20220415_20.png
如何解决iPhone X这类的问题:
第一种方法:给导航栏或tabbar增加一个固定的距离,比如顶部增加44pt(状态栏,刘海的高度),底部增加34pt.一开始有不少开发者都是这么干的,但是这样在某些状态下有一些问题:
- 如果以后摄像头做小了,上面的状态栏不是44,比方说34,可能又要增加这种情况适配.
- 还有一个横竖屏就完蛋了,本来是竖屏,转过去,应该左侧距离44,上部靠顶布置,右侧留34.下面顶住下面.如果按照写死就会导致上面 44,下面34,左右需要间距的却没有间距.不过对于手机的应用来说,大部分都不要求页面都是不让旋转屏幕的.
- 还有一个确定是不够动态,还是举个例子,假如有电话打进来了,导航栏应该会下移,这时候view可能还是会被挡住
第二种方法:使用safeAreaLayoutGuide 和 safeAreaInsets
autolayout使用系统提供的属性safeAreaLayoutGuide
1.它是UIView的一个只读属性,意味着所有UIView对象都有并且是系统帮我们创建好的
2.它继承UILayoutGuide,有layoutFrame意味着它能代表一块区域
3.它代表的区域避开了诸如导航栏、tabbar或者其他有可能挡住你这个UIView对象显示的所有父view,意味着你的view对象只要相对另一个view的safeLayoutGuide做布局就不用担心她被奇奇怪怪的东西挡住
4.对于控制器的view的safeAreaLayoutGuide,他的区域同样避开了statusbar或其他有可能挡住view显示的东西,我们甚至可以用控制器的additionalSafeAreaInsets属性,来额外指定inset
5.如果view完全在父view的安全区域内,或者view不在视图层级或屏幕上,那么view的safeAreaLayoutGuide区域其实和view自身是一样大的.
上面是对官方文档的翻译,个人理解的系统其实的解决方案是给所有的View 添加一个属性,然后所有的被创建出来的View 的safeAreaLayoutGuide都会被赋一个值,这个值是系统根据当前机型计算,都是同一个值.
这个其实就是安全区域的范围,但是并不是我们一定要按照安全区域布局,我们可用可不用,我们可以根据实际情况进行具体的选择.比方说我们有时可能是一个webview,我们完全可以忽略安全区域,让他全屏滑动.还有视频播放器,全屏播放时我们也是忽略刘海和底部bar
常见用法
在一种常见的使用场景里,以前我的某个view是相对于控制器的view做布局,现在是相对控制器view的safeAreaLayoutGuide做布局了
以前是这样写 self.vc.view
[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.vc.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
现在是这样 self.vc.view.safeAreaLayoutGuide
[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.vc.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
这里有两种情况:
1.A控制的View 加在rootViewcontroller的view 中,其实我们需要的对A控制器的View对于安全的区域作用布局就好,因为控制器的View没有被遮挡,子控件是相对于父控件布局的,自然也不会有影响.子控件对不对安全区域布局都没有影响.
2.A控制的View 加在rootViewcontroller的view 中,如果A控制器的View 没有对安全区域进行布局, 我们在创建这个View 的子控件来说,如果我们是用xib布局的,因为默认是勾选了下面的选项,子控件依然能够布局正确,按照安全区域进行布局.
Snip20220415_15.png如果我们想扩大自定义安全区域
对于上面勾选的这个safe Area Relative Margins, 网上是说可以通过在 A控制器中里面加上下面的代码,安全区域增加一个额外的Margins.
下面代码放在控制器里面:
- (void)viewDidLayoutSubviews {
self.additionalSafeAreaInsets = UIEdgeInsetsMake(50, 50, 50, 50);
}
可以使用这个属性调整安全区域,安全区域仅仅指的是不被系统的status bar或者navigation bar所遮挡,你可以通过这个属性扩大安全区域,举个例子,一个画画的app,你可以通过这个属性防止内容被工具调色板遮挡.
如果我们是Frame布局呢
如果你是使用Frame进行布局,可以利用系统提供的这个safeAreaInsets
有没有觉得和safeAreaLayoutGuide很像?safeAreaLayoutGuide可能就是根据safeAreaInsets来调整自己的bounds的
iPhone X竖屏时占满整个屏幕的控制器的view的safeAreaInsets是(44,0,34,0),横屏是(0,44,21,44),inset后的区域正好是safeAreaLayoutGuide区域
既然如此,对于自定义的顶部导航栏来说,我们可以给导航栏的高度加上一个vc.view.safeAreaInsets.top,让他变高一点就可以了,这样在X上,竖屏时top = 44, 横屏时top = 0,导航栏的高度能响应改变
需要注意的是,无论safeAreaLayoutGuide还是safeAreaInsets都是iOS11才能使用的。
对于safeAreaInsets,我们可以把版本判断写在一个函数里
我们可以这样写
static inline UIEdgeInsets sgm_safeAreaInset(UIView *view) {
if (@available(iOS 11.0, *)) {
return view.safeAreaInsets;
}
return UIEdgeInsetsZero;
}
UIEdgeInsets safeAreaInsets = sgm_safeAreaInset(self.view);
CGFloat height = kDefaultTopViewHeight; // 导航栏原本的高度,通常是44.0
height += safeAreaInsets.top > 0 ? safeAreaInsets.top : 20.0; // 20.0是statusbar的高度
问题又来了,这段代码放在什么地方合适呢?前面官方文档提到过,如果view不在屏幕上或显示层级里,view的safeAreaInsets = UIEdgeInsetsZero,所以我们需要明确知道safeAreaInsets改变的时机
应该放在下面的位置
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
UIEdgeInsets safeAreaInsets = sgm_safeAreaInset(self.view);
CGFloat height = 44.0; // 导航栏原本的高度,通常是44.0
height += safeAreaInsets.top > 0 ? safeAreaInsets.top : 20.0; // 20.0是statusbar的高度,这里假设statusbar不消失
if (_navigationbar && _navigationbar.height != height) {
_navigationbar.height = height;
}
网友评论