iOS11中safeArea及iphoneX適配的示例分析-創(chuàng)新互聯(lián)

小編給大家分享一下iOS11中safeArea及iphoneX適配的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

創(chuàng)新互聯(lián)公司于2013年開(kāi)始,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站制作、成都做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元富裕做網(wǎng)站,已為上家服務(wù),為富裕各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18982081108

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

  1. 不適合多機(jī)型的適配,如果以后出了一種帶劉海的iPad,需要預(yù)留出來(lái)的距離就未必是現(xiàn)在寫(xiě)死的距離

  2. 不適合需要支持橫豎屏的app,橫屏頂部不需要增加距離,反而是左右各有44pt,底部的距離也和豎屏不同

  3. 不夠動(dòng)態(tài)。還是舉個(gè)例子,假如有電話打進(jìn)來(lái)了,導(dǎo)航欄應(yīng)該會(huì)下移,這時(shí)候view可能還是會(huì)被擋住

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

safeAreaLayoutGuide

首先我們看看什么是safeAreaLayoutGuide

iOS11中safeArea及iphoneX適配的示例分析

看起來(lái)復(fù)雜,其實(shí)很簡(jiǎn)單,我歸納一下有幾點(diǎn):

  1. 它是UIView的一個(gè)只讀屬性,意味著所有UIView對(duì)象都有并且是系統(tǒng)幫我們創(chuàng)建好的

  2. 它繼承UILayoutGuide,有l(wèi)ayoutFrame意味著它能代表一塊區(qū)域

  3. 它代表的區(qū)域避開(kāi)了諸如導(dǎo)航欄、tabbar或者其他有可能擋住你這個(gè)UIView對(duì)象顯示的所有父view,意味著你的view對(duì)象只要相對(duì)另一個(gè)view的safeLayoutGuide做布局就不用擔(dān)心她被奇奇怪怪的東西擋住

  4. 對(duì)于控制器的view的safeAreaLayoutGuide,他的區(qū)域同樣避開(kāi)了statusbar或其他有可能擋住view顯示的東西,我們甚至可以用控制器的additionalSafeAreaInsets屬性,來(lái)額外指定inset

  5. 如果view完全在父view的安全區(qū)域內(nèi),或者view不在視圖層級(jí)或屏幕上,那么view的safeAreaLayoutGuide區(qū)域其實(shí)和view自身是一樣大的

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

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

iOS11中safeArea及iphoneX適配的示例分析

iphone X豎屏safeAreaLayoutGuide的bounds.png

iOS11中safeArea及iphoneX適配的示例分析

iPhone X橫屏safeAreaLayoutGuide的bounds.png

截圖來(lái)自https://developer.apple.com/videos/play/fall2017/801/

不過(guò)需要銘記的一點(diǎn)是這個(gè)“view”并不會(huì)顯示在我們的視圖層級(jí)上。
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.

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

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

以前是這樣寫(xiě)
[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];

適配前后的效果

iOS11中safeArea及iphoneX適配的示例分析

適配前.png

iOS11中safeArea及iphoneX適配的示例分析

改成相對(duì)于view的safeAreaLayoutGuide后-豎屏.png

iOS11中safeArea及iphoneX適配的示例分析

改成相對(duì)于view的safeAreaLayoutGuide后-橫屏.png

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

那么非iOS11怎么辦?

非iOS11 還是只能對(duì)view做布局,就要寫(xiě)兩套布局代碼,稍后會(huì)介紹

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

并不是

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

frame布局

這時(shí)就輪到safeAreaInsets來(lái)發(fā)揮作用啦

safeAreaInsets

先看一下safeAreaInsets的官方解釋

iOS11中safeArea及iphoneX適配的示例分析

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

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

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

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

我們可以這樣寫(xiě)

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的高度

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

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

對(duì)于UIViewController

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


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

對(duì)于UIView

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

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

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

以下是從“rootVC” push 到 “pushVC”控制臺(tái)輸出的調(diào)用時(shí)序以及對(duì)應(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)用時(shí)機(jī)很早,在viewWillAppear后,這是為什么出現(xiàn)多余動(dòng)畫(huà)的原因。并且“pushVC”的safeAreaInsets直到viewSafeAreaInsetsDidChange調(diào)用前,都是UIEdgeInsetsZero,之后才是正確的UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)

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

最后的代碼應(yīng)該長(zhǎ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;
 }

適配前后的效果

iOS11中safeArea及iphoneX適配的示例分析

適配前

iOS11中safeArea及iphoneX適配的示例分析

適配后

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

順便提一下,VFL似乎已經(jīng)廢了,因?yàn)閨只能表示父view的邊緣,并沒(méi)有一個(gè)符號(hào)來(lái)表示父view的safeAreaLayoutGuide的邊緣,以前我們寫(xiě)的VFL代碼,好多得改,改起來(lái)也特別麻煩,建議別再用VFL了

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

因?yàn)槲抑皩?xiě)了一個(gè)自動(dòng)布局框架,這次就將safeAreaLayoutGuide和版本判斷都順便封裝在里面了,個(gè)人覺(jué)得這套框架比NSLayoutAnchor好用,主要作用是簡(jiǎn)化布局代碼書(shū)寫(xiě),以下是生成一個(gè)NSLayoutConstraint的對(duì)比

// 需求是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

以上是“iOS11中safeArea及iphoneX適配的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站muchs.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

當(dāng)前標(biāo)題:iOS11中safeArea及iphoneX適配的示例分析-創(chuàng)新互聯(lián)
URL網(wǎng)址:http://muchs.cn/article38/hsjpp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄域名注冊(cè)、網(wǎng)站策劃網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化