欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS 11 safeArea詳解及iphoneX 適配

 更新時間:2018年02月12日 10:27:01   作者:fruitymoon  
本篇文章主要介紹了iOS 11 safeArea詳解及iphoneX 適配,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

最近看了許多iPhone X適配的文章,發(fā)現(xiàn)很少有介紹safeArea的,就來隨便寫寫

現(xiàn)在對于iPhone X的適配,有一種常見的做法是給導(dǎo)航欄或tabbar增加一個固定的距離,比如頂部增加44pt,底部增加34pt。這種寫死距離的做法乍看上去挺簡單,其實并不好,理由如下

  1. 不適合多機型的適配,如果以后出了一種帶劉海的iPad,需要預(yù)留出來的距離就未必是現(xiàn)在寫死的距離
  2. 不適合需要支持橫豎屏的app,橫屏頂部不需要增加距離,反而是左右各有44pt,底部的距離也和豎屏不同
  3. 不夠動態(tài)。還是舉個例子,假如有電話打進來了,導(dǎo)航欄應(yīng)該會下移,這時候view可能還是會被擋住

這里我想探討一下如何使用safeAreaLayoutGuide和safeAreaInsets,以一種動態(tài)的方式,一勞永逸地解決iPhone X甚至后續(xù)所有機型的適配問題。

safeAreaLayoutGuide

首先我們看看什么是safeAreaLayoutGuide

看起來復(fù)雜,其實很簡單,我歸納一下有幾點:

  1. 它是UIView的一個只讀屬性,意味著所有UIView對象都有并且是系統(tǒng)幫我們創(chuàng)建好的
  2. 它繼承UILayoutGuide,有l(wèi)ayoutFrame意味著它能代表一塊區(qū)域
  3. 它代表的區(qū)域避開了諸如導(dǎo)航欄、tabbar或者其他有可能擋住你這個UIView對象顯示的所有父view,意味著你的view對象只要相對另一個view的safeLayoutGuide做布局就不用擔(dān)心她被奇奇怪怪的東西擋住
  4. 對于控制器的view的safeAreaLayoutGuide,他的區(qū)域同樣避開了statusbar或其他有可能擋住view顯示的東西,我們甚至可以用控制器的additionalSafeAreaInsets屬性,來額外指定inset
  5. 如果view完全在父view的安全區(qū)域內(nèi),或者view不在視圖層級或屏幕上,那么view的safeAreaLayoutGuide區(qū)域其實和view自身是一樣大的

safeAreaLayoutGuide是一個相對抽象的概念,為了便于理解,我們可以把safeAreaLayoutGuide看成是一個“view”,這個“view”系統(tǒng)自動幫我們調(diào)整它的bounds,讓它不會被各種奇奇怪怪的東西擋住,包括iPhone X的劉海區(qū)域和底部的一道杠區(qū)域,可以認為在這個“view”上一定能完整顯示所有內(nèi)容。

以下綠色部分就是當前控制器view的safeAreaLayoutGuide區(qū)域

iphone X豎屏safeAreaLayoutGuide的bounds.png

iPhone X橫屏safeAreaLayoutGuide的bounds.png

截圖來自https://developer.apple.com/videos/play/fall2017/801/

不過需要銘記的一點是這個“view”并不會顯示在我們的視圖層級上。
UILayoutGuides will not show up in the view hierarchy, but may be used as items in an NSLayoutConstraint and represent a rectangle in the layout engine.

在我看來,他最大的作用是作為參照物,讓view可以相對某個view的safeAreaLayoutGuide做布局,從而保證view能正常、安全地顯示(相對的那個view不一定要是父view)

在一種常見的使用場景里,以前我的某個view是相對于控制器的view做布局,現(xiàn)在是相對控制器view的safeAreaLayoutGuide做布局了

以前是這樣寫
[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.vc.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];

現(xiàn)在是這樣
[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.vc.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];

適配前后的效果

適配前.png

改成相對于view的safeAreaLayoutGuide后-豎屏.png

改成相對于view的safeAreaLayoutGuide后-橫屏.png

可以看到,相同的布局下,橫屏在沒有statusbar時,距離頂部是0,左邊是44,如果有statusbar,距離頂部就是20。反正不管怎么弄,只要我們相對safeAreaLayoutGuide做布局,我們的view就能夠安全完整地顯示出來

那么非iOS11怎么辦?

非iOS11 還是只能對view做布局,就要寫兩套布局代碼,稍后會介紹

這樣是不是就足夠應(yīng)對所有情況了呢?

并不是

我們自定義的view有一邊需要緊挨著屏幕邊緣,比如我項目里是自定義的導(dǎo)航欄,它的頂部是挨著屏幕頂部的,那么導(dǎo)航欄就不能相對view的safeAreaLayoutGuide布局,否則頂部會空出來一截子

frame布局

這時就輪到safeAreaInsets來發(fā)揮作用啦

safeAreaInsets

先看一下safeAreaInsets的官方解釋


有沒有覺得和safeAreaLayoutGuide很像?safeAreaLayoutGuide可能就是根據(jù)safeAreaInsets來調(diào)整自己的bounds的

iPhone X豎屏?xí)r占滿整個屏幕的控制器的view的safeAreaInsets是(44,0,34,0),橫屏是(0,44,21,44),inset后的區(qū)域正好是safeAreaLayoutGuide區(qū)域

既然如此,對于自定義的頂部導(dǎo)航欄來說,我們可以給導(dǎo)航欄的高度加上一個vc.view.safeAreaInsets.top,讓他變高一點就可以了,這樣在X上,豎屏?xí)rtop = 44, 橫屏?xí)rtop = 0,導(dǎo)航欄的高度能響應(yīng)改變

需要注意的是,無論safeAreaLayoutGuide還是safeAreaInsets都是iOS11才能使用的。
對于safeAreaInsets,我們可以把版本判斷寫在一個函數(shù)里

我們可以這樣寫

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; // 導(dǎo)航欄原本的高度,通常是44.0
height += safeAreaInsets.top > 0 ? safeAreaInsets.top : 20.0; // 20.0是statusbar的高度

問題又來了,這段代碼放在什么地方合適呢?前面官方文檔提到過,如果view不在屏幕上或顯示層級里,view的safeAreaInsets = UIEdgeInsetsZero,所以我們需要明確知道safeAreaInsets改變的時機

實際上系統(tǒng)已經(jīng)提供了回調(diào)

對于UIViewController

復(fù)制代碼 代碼如下:

-(void)viewSafeAreaInsetsDidChange NS_REQUIRES_SUPER API_AVAILABLE(ios(11.0), tvos(11.0));

對于UIView

-(void)safeAreaInsetsDidChange API_AVAILABLE(ios(11.0),tvos(11.0));

這里主要探討controller的回調(diào),view的回調(diào)是類似的。只要controller的view的safeAreaInsets改變,系統(tǒng)就會調(diào)用viewSafeAreaInsetsDidChange。自然而然,我們會想把以上代碼放在這里,然而這里有個大坑,你會發(fā)現(xiàn),當這個控制器以動畫的方式push進來時,導(dǎo)航欄的高度也會動畫地變高,產(chǎn)生了不必要的多余動畫,這種體驗很糟糕

那么究竟應(yīng)該放在哪里?我們很有必要看一下新的viewController調(diào)用時序

以下是從“rootVC” push 到 “pushVC”控制臺輸出的調(diào)用時序以及對應(yīng)控制器的view的safeAreaInsets

2017-10-04 16:59:59.594811+0800 XXX[15662:803767] Begin pushViewController to [<_TtCC8XXXTests27ContainerViewControllerTest20MockUIViewController: 0x7f9c07b643b0>]
viewDidLoad()---Optional("pushVC")---UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
willMove(toParentViewController:)---Optional("pushVC")---UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
viewWillDisappear---Optional("rootVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewWillAppear---Optional("pushVC")---UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
viewSafeAreaInsetsDidChange()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewWillLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewWillLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidAppear---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidDisappear---Optional("rootVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
didMove(toParentViewController:)---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
2017-10-04 16:59:59.604563+0800 XXX[15662:803767] Did PushViewController [<_TtCC8XXXTests27ContainerViewControllerTest20MockUIViewController: 0x7f9c0790d170>]->[<_TtCC8XXXTests27ContainerViewControllerTest20MockUIViewController: 0x7f9c07b643b0>] time = [0.009772]

可以看到,viewSafeAreaInsetsDidChange調(diào)用時機很早,在viewWillAppear后,這是為什么出現(xiàn)多余動畫的原因。并且“pushVC”的safeAreaInsets直到viewSafeAreaInsetsDidChange調(diào)用前,都是UIEdgeInsetsZero,之后才是正確的UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)

并且viewSafeAreaInsetsDidChange后面會調(diào)用兩次viewDidLayoutSubviews,所以我們應(yīng)該把改變高度或布局的代碼都寫在viewDidLayoutSubviews里,這樣就不會有多余的動畫效果了。需要注意viewDidLayoutSubviews可能會由別的操作頻繁觸發(fā),所以如果調(diào)整safeArea布局的代碼比較耗時,可以考慮加上一個狀態(tài)標記,只在didChange后執(zhí)行一次布局調(diào)整

最后的代碼應(yīng)該長這樣

- (void)viewDidLayoutSubviews {
 [super viewDidLayoutSubviews];
 UIEdgeInsets safeAreaInsets = sgm_safeAreaInset(self.view);
 CGFloat height = 44.0; // 導(dǎo)航欄原本的高度,通常是44.0
 height += safeAreaInsets.top > 0 ? safeAreaInsets.top : 20.0; // 20.0是statusbar的高度,這里假設(shè)statusbar不消失
 if (_navigationbar && _navigationbar.height != height) {
  _navigationbar.height = height;
 }

適配前后的效果

適配前

適配后

這樣對于frame布局和autolayout布局的各種情況,有了一個動態(tài)的適配方案,就是分別使用safeAreaLayoutGuide和safeAreaInsets來靈活處理布局,相比寫死一個固定距離,當前和未來的各種機型都能一套代碼適配,擴展性更好。我們項目目前也是采用這種做法,如果你的項目需要適配橫豎屏或UI控件布局相對復(fù)雜,真的應(yīng)該考慮使用safeArea

順便提一下,VFL似乎已經(jīng)廢了,因為|只能表示父view的邊緣,并沒有一個符號來表示父view的safeAreaLayoutGuide的邊緣,以前我們寫的VFL代碼,好多得改,改起來也特別麻煩,建議別再用VFL了

最后一個版本判斷的問題,safeAreaInsets和safeAreaLayoutGuide都是iOS11的API,如果不做封裝,直接在代碼里寫,勢必會出現(xiàn)大量@available這種版本判斷語句,代碼里到處是@available,看起來很崩潰,破壞代碼可讀性。

因為我之前寫了一個自動布局框架,這次就將safeAreaLayoutGuide和版本判斷都順便封裝在里面了,個人覺得這套框架比NSLayoutAnchor好用,主要作用是簡化布局代碼書寫,以下是生成一個NSLayoutConstraint的對比

// 需求是topLeftView的top等于self.view的safeAreaLayoutGuide的top
// 使用系統(tǒng)API
if (@available(iOS 11.0, *)) {
  [NSLayoutConstraint constraintWithItem:self.topLeftView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
 } else {
  [NSLayoutConstraint constraintWithItem:self.topLeftView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
 }

// 使用NSLayoutConstraint-SSLayout
self.topLeftView.top_attr = self.view.top_attr_safe

在業(yè)務(wù)代碼里不會出現(xiàn)任何版本判斷,大家有興趣的話可以試一下

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • iOS 11 UINavigationItem 去除左右間隙的方法

    iOS 11 UINavigationItem 去除左右間隙的方法

    本篇文章主要介紹了iOS 11 UINavigationItem 去除左右間隙的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • IOS開發(fā)環(huán)境windows化攻略

    IOS開發(fā)環(huán)境windows化攻略

    本人主要介紹了IOS開發(fā)環(huán)境windows化攻略,需要的朋友可以參考下
    2013-06-06
  • 詳解iOS項目基本框架搭建

    詳解iOS項目基本框架搭建

    本篇文章給讀者們詳細分析了iOS項目基本框架搭建的過程的注意點,對此有需要的朋友學(xué)習(xí)參考下。
    2018-02-02
  • iOS判斷是否越獄設(shè)備方法示例

    iOS判斷是否越獄設(shè)備方法示例

    這篇文章主要給大家介紹了關(guān)于iOS判斷是否越獄設(shè)備的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對各位iOS開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • iOS 9 Core Spotlight搜索實例代碼

    iOS 9 Core Spotlight搜索實例代碼

    本文主要講解 iOS 9 Core Spotlight,在 IOS 開發(fā)的時候有時候會用到搜索功能,這里給大家一個實例作為參考,有需要的小伙伴可以參考下
    2016-07-07
  • iOS中searchBar(搜索框)光標初始位置后移

    iOS中searchBar(搜索框)光標初始位置后移

    這篇文章主要介紹了iOS中searchBar(搜索框)光標初始位置后移的關(guān)鍵代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-08-08
  • iOS自定義UIButton點擊動畫特效

    iOS自定義UIButton點擊動畫特效

    這篇文章主要為大家詳細介紹了iOS自定義UIButton點擊動畫特效,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • iOS 10 推送高階篇(必看)

    iOS 10 推送高階篇(必看)

    本文重點給大家介紹ios10 推送的所有通知類,所以說這篇教程是ios10推送必看篇,感興趣的朋友快來一起學(xué)習(xí)吧
    2016-09-09
  • IOS開發(fā)使用KeychainItemWrapper 持久存儲用戶名和密碼

    IOS開發(fā)使用KeychainItemWrapper 持久存儲用戶名和密碼

    這篇文章主要介紹了IOS開發(fā)使用KeychainItemWrapper 持久存儲用戶名和密碼的相關(guān)資料,需要的朋友可以參考下
    2015-11-11
  • iOS延遲執(zhí)行方法詳解

    iOS延遲執(zhí)行方法詳解

    這篇文章主要為大家詳細介紹了iOS延遲執(zhí)行方法,包括performSelector(NSObject)方法、NSTimer、GCD和sleep(NSThread)四種方法,需要的朋友可以參考下
    2016-11-11

最新評論