iOS safeAreaInsets相关知识

1: 前言

iOS 11系统发布后, UIView多了几个安全区域相关的属性和方法, 用于界面适配, 如:safeAreaInsets, safeAreaLayoutGuide,insetsLayoutMarginsFromSafeArea,以及safeAreaInsetsDidChange方法;

理解:
在iOS11前, 作界面适配时, 若是界面上有导航栏时, 若是想作到界面不被导航栏遮盖住, 须要在设置约束时, 可将frame.origin.y设置为0,如:
self.bgView.frame = CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height);
(bgView是一个控件)
当iPhoneX系列没发布前, 其余全部iPhone机型的导航栏, 状态栏, tabbar栏, 高度都是同样, 作适配的时候很简单.可是iPhoneX系列发布后, 出现了新的状态栏, tabbar栏高度, 致使适配工做量加大,因此官方新增了safeAreaInsets等属性,方便界面适配.

复制代码

2: safeAreaInsets

2.1  简述safeAreaInsets的上下左右的距离

safeArea是指没有被状态栏, 导航栏, tabbar栏, toolbars, 或其余视图控制器遮盖的区域, 经过safeAreaInsets能够获取到视图的安全距离. 可是若是一个view没有在视图层次结构中或未在屏幕上显示, 则safeAreaInsets为0;

示例一: 在iPhone6s上测试一个控件的安全距离(iOS系统为12.0)
- (void)viewDidLoad {
    [super viewDidLoad];
    if (@available(iOS 11.0, *)) {//viewDidLoad
           NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
           NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
           NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
           NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (@available(iOS 11.0, *)) {
        NSLog(@"2 --- viewWillAppear");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    if (@available(iOS 11.0, *)) {
        NSLog(@"3 --- viewSafeAreaInsetsDidChange");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    if (@available(iOS 11.0, *)) {//viewWillLayoutSubviews
        NSLog(@"4 --- viewWillLayoutSubviews");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    if (@available(iOS 11.0, *)) {//viewDidLayoutSubviews
        NSLog(@"5 --- viewDidLayoutSubviews");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (@available(iOS 11.0, *)) {
        NSLog(@"6 --- viewDidAppear");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}


输出结果:
1 --- viewDidLoad
self.view.safeAreaInsets.top = 0.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 0.000000
self.view.safeAreaInsets.right = 0.000000

2 --- viewWillAppear
self.view.safeAreaInsets.top = 0.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 0.000000
self.view.safeAreaInsets.right = 0.000000

3 --- viewSafeAreaInsetsDidChange
self.view.safeAreaInsets.top = 64.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 49.000000
self.view.safeAreaInsets.right = 0.000000

4 --- viewWillLayoutSubviews
self.view.safeAreaInsets.top = 64.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 49.000000
self.view.safeAreaInsets.right = 0.000000

5 --- viewDidLayoutSubviews
self.view.safeAreaInsets.top = 64.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 49.000000
self.view.safeAreaInsets.right = 0.000000

6 --- viewDidAppear
self.view.safeAreaInsets.top = 64.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 49.000000
self.view.safeAreaInsets.right = 0.000000

示例二: 在iPhone6s上测试一个控件的安全距离(iOS系统为9.3)
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1 --- viewDidLoad");
    NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
    NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
    NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
    NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
}

结果: 代码直接崩溃, 报错信息:-[UIView safeAreaInsets]: unrecognized selector sent to instance 0x7fdc674467c0

示例三: 在iPhoneX上测试一个控件的安全距离
- (void)viewDidLoad {
    [super viewDidLoad];
    if (@available(iOS 11.0, *)) {
        NSLog(@"1 --- viewDidLoad");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (@available(iOS 11.0, *)) {
        NSLog(@"2 --- viewWillAppear");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    if (@available(iOS 11.0, *)) {
        NSLog(@"3 --- viewSafeAreaInsetsDidChange");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    if (@available(iOS 11.0, *)) {//viewWillLayoutSubviews
        NSLog(@"4 --- viewWillLayoutSubviews");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    if (@available(iOS 11.0, *)) {
        NSLog(@"5 --- viewDidLayoutSubviews");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (@available(iOS 11.0, *)) {
        NSLog(@"6 --- viewDidAppear");
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
    }
}

输出结果:
1 --- viewDidLoad
self.view.safeAreaInsets.top = 0.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 0.000000
self.view.safeAreaInsets.right = 0.000000

2 --- viewWillAppear
self.view.safeAreaInsets.top = 0.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 0.000000
self.view.safeAreaInsets.right = 0.000000

3 --- viewSafeAreaInsetsDidChange
self.view.safeAreaInsets.top = 88.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 83.000000
self.view.safeAreaInsets.right = 0.000000

4 --- viewWillLayoutSubviews
self.view.safeAreaInsets.top = 88.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 83.000000
self.view.safeAreaInsets.right = 0.000000

5 --- viewDidLayoutSubviews
self.view.safeAreaInsets.top = 88.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 83.000000
self.view.safeAreaInsets.right = 0.000000

6 --- viewDidAppear
self.view.safeAreaInsets.top = 88.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 83.000000
self.view.safeAreaInsets.right = 0.000000


小结:
(1)view没有在视图层次结构中或未在屏幕上显示时, safeAreaInsets为0;
(2)矩形或刘海屏手机均有安全距离, 只要iOS系统大于或等于11;
(3)若是安全距离存在, 能够经过safeAreaInsets获取到视图的安全距离;
复制代码

2.2  控制器的rootView的安全区域

对于一个viewController的root view, safeArea指的是未被状态栏、一些可见的bars和经过additionalSafeAreaInsets属性设置的值遮盖的区域; 安全

                  (图片来自于网络)

2.3   父视图下的子视图的安全距离

对于在视图层次结构中的其余视图, safeArea指的是未被状态栏、navigation bars、tabbar、toolbars或其余视图控制器遮盖的区域。 例如: 若是一个视图彻底在它父视图的范围内, 那么safeAreaInsets为0;若是其超出父视图的安全范围,那么safeAreaInsets按照被遮住的大小计算。bash

示例一:vc的rootView是redView, redView有个subView是yellowView, 当yellowView不处于redView的安全区域以内时: 网络

             yellowView是redView的子控件

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    if (@available(iOS 11.0, *)) {
        //打印redView的安全距离(redView就是vc的root View)
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
        
        //打印yellowView的安全距离
        NSLog(@"self.yellowView.safeAreaInsets.top = %f",self.yellowView.safeAreaInsets.top);
        NSLog(@"self.yellowView.safeAreaInsets.left = %f",self.yellowView.safeAreaInsets.left);
        NSLog(@"self.yellowView.safeAreaInsets.bottom = %f",self.yellowView.safeAreaInsets.bottom);
        NSLog(@"self.yellowView.safeAreaInsets.right = %f",self.yellowView.safeAreaInsets.right);
    }
}

输出结果:
self.view.safeAreaInsets.top = 44.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 34.000000
self.view.safeAreaInsets.right = 0.000000


self.yellowView.safeAreaInsets.top = 44.000000
self.yellowView.safeAreaInsets.left = 0.000000
self.yellowView.safeAreaInsets.bottom = 34.000000
self.yellowView.safeAreaInsets.right = 0.000000
复制代码

从输出结果能够看出,若是子视图超出父视图的安全范围, 那么safeAreaInsets按照被遮住的大小计算;ide


示例二:vc的rootView是redView, redView有个subView是yellowView, 当yellowView处于redView的安全区域以内时: 测试

             yellowView是redView的子控件

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    if (@available(iOS 11.0, *)) {
        NSLog(@"self.view.safeAreaInsets.top = %f",self.view.safeAreaInsets.top);
        NSLog(@"self.view.safeAreaInsets.left = %f",self.view.safeAreaInsets.left);
        NSLog(@"self.view.safeAreaInsets.bottom = %f",self.view.safeAreaInsets.bottom);
        NSLog(@"self.view.safeAreaInsets.right = %f",self.view.safeAreaInsets.right);
        
        NSLog(@"self.yellowView.safeAreaInsets.top = %f",self.yellowView.safeAreaInsets.top);
        NSLog(@"self.yellowView.safeAreaInsets.left = %f",self.yellowView.safeAreaInsets.left);
        NSLog(@"self.yellowView.safeAreaInsets.bottom = %f",self.yellowView.safeAreaInsets.bottom);
        NSLog(@"self.yellowView.safeAreaInsets.right = %f",self.yellowView.safeAreaInsets.right);
    }
}

输出结果:
self.view.safeAreaInsets.top = 44.000000
self.view.safeAreaInsets.left = 0.000000
self.view.safeAreaInsets.bottom = 34.000000
self.view.safeAreaInsets.right = 0.000000

self.yellowView.safeAreaInsets.top = 0.000000
self.yellowView.safeAreaInsets.left = 0.000000
self.yellowView.safeAreaInsets.bottom = 0.000000
self.yellowView.safeAreaInsets.right = 0.000000
复制代码

从输出结果能够看出视图处于父视图的安全区域中时, safeAreaInsets为0;ui

2.4  修改安全区域

若是对系统所提供的安全区域不满意, 还能够经过additionalSafeAreaInsets属性来修改安全区域, 示例一:spa

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.redColor;
    //修改安全区域  
    self.additionalSafeAreaInsets = UIEdgeInsetsMake(100, 30, 100, 40);
}

输出结果:
self.view.safeAreaInsets.top = 144.000000
self.view.safeAreaInsets.left = 30.000000
self.view.safeAreaInsets.bottom = 134.000000
self.view.safeAreaInsets.right = 40.000000

复制代码

从输出接口能够看出:
(1)经过additionalSafeAreaInsets能够修改安全区域的大小;
(2)修改的安全区域的大小时,是在原来的安全区域的基础上作出修改的;code

由此能够看出,safeAreaInsets指的就是一个控件可见区域距离屏幕上下左右边的距离。cdn

2.5   作屏幕适配

一个控制器从建立到界面显示, 会依次调用如下方法:
- (void)viewDidLoad;
- (void)viewWillAppear:(BOOL)animated;
- (void)viewSafeAreaInsetsDidChange;
- (void)viewWillLayoutSubviews;
- (void)viewDidLayoutSubviews;
- (void)viewDidAppear:(BOOL)animated;

在调用- viewSafeAreaInsetsDidChange方法时, 界面的safeAreaInsets值会被计算出来,在这个方法中能够更改控件的位置;

复制代码

参考:www.jianshu.com/p/671e70d8d…blog

相关文章
相关标签/搜索