iOS11 系统发布后,UIView多了几个与安全区域相关的属性和方法,用于界面适配,如:safeAreaInsets、safeAreaLayoutGuide、insetsLayoutMarginsFromSafeArea,以及safeAreaInsetsDidChange方法,初看到这些时我眼花缭乱,不知其存在的意义何在.扪参历井仰胁息,以手抚膺坐长叹.这特么到底怎么用啊???经过不懈的努力,阅读开发文档、测试,我明了了.
以下是我的理解.
在iOS11前的系统,做界面适配时,如果界面上有导航栏时,想做到界面不被导航栏遮盖住,需要将控件的frame.origin.y值加上导航栏和状态栏的高度,也就是frame.origin.y-64.当iPhoneX系列没发布前,其他所有iPhone机型的导航栏、状态栏、tab栏,高度都一样,做适配时很简单.但是iPhoneX系列发布后,出现了新的状态栏、tab栏高度,导致适配工作量加大.所以官方新增了safeAreaInsets等属性,方便界面适配.
safeAreaInsets
safeArea是指没有被navigation bars, tab bars, toolbars,或其他视图控制器遮盖的区域,通过safeAreaInsets可以获取到视图的安全距离.但是如果一个view没有在视图层次结构中或未在屏幕上显示,则safeAreaInsets为0.
对于一个VC的root view,safeArea指的是未被状态栏、一些可见的bars、和通过additionalSafeAreaInsets属性设置的值遮盖的区域,(注意下面iPhoneX的安全区域)
VC的rootView的safeArea.png
对于在视图层次结构中的其他视图,safeArea指的是未被navigation bars, tab bars, toolbars,或其他视图控制器遮盖的区域;例如如果一个视图的完全在它父视图的范围内,那么safeAreaInsets为0;如果其超出父视图的安全范围,那么safeAreaInsets按照被遮住的大小计算.例如:
下面是一个vc, vc的rootView是redView,redView有个subView是yellowView,当yellowView不处于redView的安全区域之内时:
yellowView not in redView's safeArea
yellowView.safeAreaInsets=UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
redView.safeAreaInsets=UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
当yellowView处在redView的安全区域内时:
image.png
yellowView.safeAreaInsets=UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
redView.safeAreaInsets=UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
如果对系统所提供的安全区域不满意,还可以通过additionalSafeAreaInsets属性来修改安全区域,例如设置 vc.additionalSafeAreaInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0),相应的
redView.safeAreaInsets=UIEdgeInsets(top: 52.0, left: 8.0, bottom: 42.0, right: 8.0)
说白了safeAreaInsets 指的就是一个控件可见区域距离屏幕上下左右边的距离.
那么在做适配时,这个属性怎么用呢?
一个vc从创建到界面显示,会依次调用以下方法:
viewDidLoad ->viewWillAppear->viewSafeAreaInsetsDidChange->viewWillLayoutSubviews->viewDidLayoutSubviews->viewDidAppear
在viewSafeAreaInsetsDidChange 方法时界面的safeAreaInsets值会被计算出来,在这个方法中可以更改控件位置:
override func viewSafeAreaInsetsDidChange() {
if #available(iOS 11.0, *) {
self.yellowView.frame = CGRect.init(x: 0, y: self.view.safeAreaInsets.top, width: self.view.frame.size.width, height: self.view.frame.size.height - self.view.safeAreaInsets.top - self.view.safeAreaInsets.bottom)
} else {
// Fallback on earlier versions
}
}
safeAreaLayoutGuide
如果不用硬编码形式设置位置,而是使用autolayout布局,safeAreaLayoutGuide属性会更有用.
safeAreaLayoutGuide是UILayoutGuide类型,适用于autolayout,而safeAreaInsets是UIEdgeInsets类型,适用于用frame设置位置
override func viewDidLoad() {
yellowView.snp.makeConstraints { (make) in
if #available(iOS 11.0, *) {
make.edges.equalTo(self.view.safeAreaLayoutGuide)
} else {
make.edges.equalTo(UIEdgeInsets.zero)
}
}
}
注意: 如果在viewDidLoad不能用make.edges.equalTo(self.view.safeAreaInsets) 设置边距,在viewDidLoad方法中safeAreaInsets为0.
insetsLayoutMarginsFromSafeArea
一个布尔值,指示是否自动更新视图的布局边距以反映安全区域,默认为yes.
单看这个属性,就知道其与layoutMargins、safeAreaInsets属性有关,事实也确实如此.
iOS中layoutMargins 默认为 UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
当insetsLayoutMarginsFromSafeArea == true时:
layoutMargins = layoutMargins + safeAreaInsets;
例如:
在vc中更改安全区域大小,并设置yellowView充满全屏:
if #available(iOS 11.0, *) {
self.additionalSafeAreaInsets = UIEdgeInsets.init(top: 50, left: 50, bottom: 50,
right: 50)
} else {
// Fallback on earlier versions
}
yellowView.snp.makeConstraints { (make) in
make.edges.equalTo(UIEdgeInsets.zero)
}
此时,yellowView未处于redView的安全区域内,此时yellowView.safeAreaInsets=UIEdgeInsets(top:94.0, left: 50.0, bottom: 84.0, right:50.0)
yellowView.layoutMargins = UIEdgeInsets(top:102.0, left: 58.0, bottom: 92.0, right:58.0)
在yellowView中添加blueView,blueView的适配如下:
blueView.snp.makeConstraints { (make) in
if #available(iOS 9.0, *) {
make.edges.equalTo(self.layoutMarginsGuide)
} else {
// Fallback on earlier versions
}
}
image.png
此时blueView.frame = (58 102; 298 702),受到了安全区域的影响;如果想不受到安全区域的影响,只需要设置yellowView.insetsLayoutMarginsFromSafeArea = false 即可
综上,就是我对安全区域的理解,及如何运用.
网友评论