(備注:本文由本人翻譯自Darryl Pogue的Understanding the WebView Viewport in iOS 11)
下面為正文內(nèi)容:

發(fā)布于09/13/2017,作者:Darryl Pogue
iOS11在頂部狀態(tài)欄區(qū)域帶來了一些新的可能不太直觀的行為改變,但是這對那些使用如Apache Cordova或Ionic等工具的開發(fā)人員來說非常重要。值得說明的是,這種改變將會影響任何基于Web的使用了固定狀態(tài)欄的應用程序,當開發(fā)人員意圖為iOS11構(gòu)建這些應用程序時。那么此篇文章將會幫助您了解iOS11中的WebView組件。
注意:現(xiàn)有的應用程序依然將正常工作,因為其內(nèi)部的WebView的組件行為并未改變。這種改變只影響使用Xcode9編譯適配iOS11的應用程序。
為了理解這種變化,我們來看看歷史版本中狀態(tài)欄的變化。
狀態(tài)欄與安全區(qū)域
在早期版本的iOS中,狀態(tài)欄僅僅是屏幕頂部不變的不可觸控的黑色條。對開發(fā)人員來說狀態(tài)欄不過是一個系統(tǒng)UI,應用程序在它下面的空間運行而已。
這種情況隨著iOS7的到來有所變化,iOS7中的狀態(tài)欄變成了透明的,并根據(jù)導航欄(Navigation Bar)的顏色變化而變化。它意味著如Cordova這樣的WebView組件中的應用程序,必須檢測運行環(huán)境的iOS版本,并將20px的內(nèi)邊距固定添加到頁面的頂部,來保證下面內(nèi)容的顯示位置正確。
而在iOS7以后的版本中狀態(tài)欄只是增加了一些小的修正,包括增加了額外的橫幅在通話時或者后臺使用地理定位時。
對于本地應用來說,這部分是通過UINavigationBar和autolayout原生控件自動處理的。這些原生組件中的布局規(guī)則會保證內(nèi)容調(diào)整到適配狀態(tài)欄的正確高度,從而顯示在狀態(tài)欄不會遮擋它們的所謂“安全區(qū)域”中。同時,如果你的導航欄(UINavigationBar)靠部對其,它的顏色就會自動延伸到狀態(tài)欄后面,而形成統(tǒng)一的顏色。可不幸的是,對于基于Web的應用程序確不能享用這份福利。
iOS11中的改變

iOS11中不同的是WebView中的內(nèi)容現(xiàn)在也增加了對這個所謂的“安全區(qū)域”的支持。它意味著當你的頁面中有個固定位置的標題欄元素(設置為top:0),那它在頁面渲染完成后會出現(xiàn)在屏幕頂部的20px以下也就是對齊了狀態(tài)欄的地方。但是,值得注意的是:當內(nèi)容向下滾動時,內(nèi)容會移動到狀態(tài)欄的后面;而內(nèi)容向上滾動時,將會再次跌落到狀態(tài)欄的下方。(留下一個很尷尬的差距落差效果,原文是個視頻,這里做了些截圖。)




蘋果為什么要這樣設計?
我想你應該看過了PhoneX的設計,它有著不規(guī)則的屏幕形狀,在屏幕頂部切除了一塊區(qū)域用來放置揚聲器和相機。那么如果將頁面的元素固定到真實屏幕的頂部,將會出現(xiàn)部分內(nèi)容被放置在這塊切除區(qū)域中,而導致根本無法觸控使用。通過系統(tǒng)將它對齊到狀態(tài)欄的底部,可以確保頂部標題欄中的全部內(nèi)容都可以使用。
這很酷……除了現(xiàn)有的應用程序中出現(xiàn)的那20px的尷尬……
iOS11中的修正
幸運的是,蘋果為開發(fā)者提供了一種通過元標簽(head中的meta)來控制這個行為的方法。更幸運的是,蘋果甚至將這個修復補丁更新到了已經(jīng)被放棄的UIWebView控件中。
這個可設置的屬性是viewport-fit,它有三個可能的值:
- contain:視圖窗口應包含全部網(wǎng)頁內(nèi)容,即內(nèi)容中的固定元素將被自動包含在所謂的“安全區(qū)域”中。
- cover:網(wǎng)頁中需要處理全部視圖窗口即真實屏幕中的內(nèi)容,即你的頂部元素可能會被遮擋,你要自己處理就像在iOS10上面一樣。
- auto:默認值,與contain相同
因此,你要想將你的標題欄還原到最頂部在狀態(tài)欄的后面,就像你在iOS10中做的,你要在你的頁面的head的meta中的viewport設置中增加viewport-fit=cover。

iPhoneX
但是對于iPhoneX這種不規(guī)則的屏幕怎么辦呢?在iPhoneX上狀態(tài)欄不再是20像素高,并且因為揚聲器和相機的遮擋,你的標題欄將完全無法觸控使用了。需要注意的是,這種情況同樣會出現(xiàn)在固定在底部的頁腳部分,它將被麥克風遮擋住。
注意:如果你的應用程序中使用了Launch Storyboard方式,那你的應用程序就只能使用iPhoneX的全屏幕控件?,F(xiàn)有的應用程序?qū)⒆詣颖伙@示在頂部和底部之間的矩形空間中。(這個注意不是特別理解,感覺是啟動時用了Launch Storyboard,就是全屏幕顯示也就是WebView可以顯示全屏幕;以前編譯的應用會自動夾在中間區(qū)域,無法撐滿全屏)

不過幸運的是,蘋果公司增加了一個方法,將這個“安全區(qū)域”的布局規(guī)范開放到了CSS中。它被作為一種常量設置被添加到了CSS中,可以通過CSS中的constant()函數(shù)來訪問,并且這個函數(shù)已經(jīng)被提交給了CSS工作組進行了標準化。
這4個常量分別為:
- constant(safe-area-inset-top):獲取頂部安全區(qū)域插入值(單位為像素)
- constant(safe-area-inset-bottom):獲取底部安全區(qū)域插入值(單位為像素)
- constant(safe-area-inset-left):獲取左側(cè)安全區(qū)域插入值(單位為像素)
- constant(safe-area-inset-right):獲取右側(cè)安全區(qū)域插入值(單位為像素)
而蘋果還給了我們一個禮物就是這個變量也被更新到了UIWebView中。
使用示例
假設你的頁面上有一個固定位置的標題欄,在iOS10中的設置是這樣的:
header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 44px;
padding-top: 20px; /* 狀態(tài)欄高度 */
}
那么,要為iPhoneX或者其他iOS11的設備進行調(diào)整,你需要在頁面head中的viewport meta中增加:
<meta name="viewport" content=".... viewport-fit=cover">
更改CSS設置為:
header {
/* ... */
/* iOS10中狀態(tài)欄的高度 */
padding-top: 20px;
/* iOS11+中狀態(tài)欄的高度 */
padding-top: constant(safe-area-inset-top);
}

需要注意的是,對于不知道如何解析constant語法的舊設備來說,保留返回值是很重要的(我的理解就是指上面的padding-top:20px;)。另外,你還可以在CSS的calc()方法中使用這些常量。
如果你有底部導航欄的話,也要記得為它設置這個樣式內(nèi)容。
(這個是筆者的鳴謝,咱也替廣大開發(fā)者謝謝了?。?/p>
特此感謝蘋果公司的WebKit團隊的Timothy Horton研發(fā)了本文提到的viewport-fit和constant()功能。感謝Shazron,Julio,Kerri,Greg和Mike在測試和驗證中提供的貢獻。
(譯文完畢!)