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

相關文章
相關標籤/搜索