1. 程式人生 > >[Cordova] 移動App 的 ios11 和 iPhoneX 適配

[Cordova] 移動App 的 ios11 和 iPhoneX 適配

Apple每次退出新尺寸的iphone都會掀起一番適配風波,這次沒有下巴但有劉海的iPhoneX更是如此,網傳橫屏下的適配動畫更是令不少人汗顏。

其實對於Native App來說,適配並不算困難(當然追求酷炫效果另算),官方文件有詳細的說明,而對於Web App來說,主要還是依靠開啟webview的Native App來適配,而這篇文章主要討論的是Cordova App要如何適配iPhoneX.

沒有適配的效果:

1、以前上架的應用,目標編譯平臺ios10,上面的劉海和下面的橫杆自動被黑邊覆蓋
這裡寫圖片描述

2、目標平臺ios11,重新編譯的,上下除了黑邊還有白邊
這裡寫圖片描述

適配步驟如下

更新cordova外掛

每次大版本更新和適配,首先都是更新 Cordova 建立,確認使用的 cordova 外掛有是否有新的改進, 例如 cordova-plugin-splashscreen, cordova-plugin-statusbar 等

更新 HTML viewport meta

meta 標籤中 新增 viewport-fit=cover,這是 ios 11 新增的設定,可以讓頁面全屏展示。

<meta name="viewport" content="initial-scale=1, width=device-width, height=device-height, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover"
>

不過我測試的結果和上圖一樣,仍然有黑邊有白邊
這裡寫圖片描述

更新啟動圖

<platform name="ios">
  <splash src="res/screen/ios/[email protected]~universal~anyany.png" />
</platform>

效果如下:
這裡寫圖片描述
全屏是全屏了,但是還是有白邊

更換舊的 UIWebview 為 WKWebview

更換 webview 只要新增 cordova-plugin-wkwebview-engine 外掛
更換完,還是有點問題,底部有空白,頁面高度不是100%。但是如果換成

cordova-plugin-ionic-webview (這是 ionic 開發的 WKWebview 外掛的一個變種,主要也是解決 file 跨域的問題),頁面是可以全屏的。

於是,參考 cordova-plugin-ionic-webview 修改 cordova-plugin-wkwebview-engine\src\ios\CDVWKWebViewEngine.m,新增一點程式碼:

// re-create WKWebView, since we need to update configuration
WKWebView* wkWebView = [[WKWebView alloc] initWithFrame:self.engineWebView.frame configuration:configuration];
//------------added begin-------------
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
if (@available(iOS 11.0, *)) {
  [wkWebView.scrollView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
}
#endif
//------------added end----------------
wkWebView.UIDelegate = self.uiDelegate;
self.engineWebView = wkWebView;

然後就可以全屏了
這裡寫圖片描述

Ajax 請求全部失敗了

更換完 WKWebview,發現所有的請求都失敗了。
這是因為 WKWebview 的安全機制:
1、file:// 協議下(app內頁面index.html是 file:// 協議瀏覽的),訪問 http/https資源是屬於跨域,服務端必須實現CORS 跨域支援才可以。
可是我已經吧服務端實現過CORS了啊,為啥不行?原來是CORS實現不完整,預檢請求(Options)沒通過
本人公司因為某些原因,改服務端的辦法不可取。

2、即使服務端實現了跨域,應用發出的請求頭“Origin”是空值。

所幸的是,Oracle 開發了一個 Cordova 外掛 cordova-plugin-wkwebview-file-xhr,解決了上面提到的2個問題。
本外掛的原理是攔截所有 webview 的請求(XMLHttpRequest),用原生的方式向伺服器請求,然後把結果交給 webview。
不過外掛預設只攔截 https 請求,如果需要攔截 http 請求,需要增加配置:

<preference name="InterceptRemoteRequests" value="all" />

圖片顯示不出來了

真機上,圖片又顯示不出來了。
本來以為是圖片下載失敗,除錯發現,圖片已經下載下來了,但是 WKWebview 顯示不出已經下載好的本地圖片。
然而,模擬器確是正常的。
Google 了一大圈,發現如果圖片檔案存在的是 Tmp 目錄下,也就是 file:///var/mobile/Applications/Container/Data/<GUID of app>/tmp/ 目錄,則真機上可以顯示圖片,其他目錄就不行。
那就換成 Tmp 目錄。
注:Cordova 可以使用 cordova.file.tempDirectory 這個常量來得到 Tmp 目錄的具體路徑。

“劉海”適配

Safe-Area,安全區域是 iPhone X 畸形全面屏的產物。雖說是全面屏,但是頂部多了感測器(劉海)和底部 home 鍵橫線,為了讓你的應用在 iPhone X 上 面完美執行,你需要將可視元素擴充套件至填充整個展示視窗(螢幕)上,同時,你也需要保證如按鈕、tab bar 等可互動控制元件,以及一些至關重要的資訊不會因為螢幕的圓角而被裁掉,或者被手機的「劉海」和虛擬「Home鍵」遮住。

利用 蘋果提供的 CSS 常量 env(safe-area-inset-*),可以將重要的介面元素避開劉海、圓角和橫線。

Safe-Area 的適配,詳細方法自己百度。

最終結果如下:
這裡寫圖片描述

其他要注意的

如果用的是 Sencha Touch

框架會對 index.html 頁面新增2次 <meta name="viewport">
所以我們無需在 index.html 中手動增加 viewport-fit="cover",而是在框架程式碼中的一處加上 viewport-fit="cover",並且我們需要刪掉框架程式碼中的一處 ,否則 env(safe-area-inset-*) 這些 CSS 常量無效(即都是0畫素)。

  • 要刪除的地方:
    檔案 touch\src\core\Ext-more.js,刪除下面的內容:
/*if (navigator.standalone) {
    addMeta('viewport', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0');
}
else {
    addMeta('viewport', 'initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, minimal-ui');
}
addMeta('apple-mobile-web-app-capable', 'yes');
addMeta('apple-touch-fullscreen', 'yes');*/

不嫌麻煩,可以把 touch 下 sencha-touch.js、sencha-touch-all.js、sencha-touch-all-debug.js、sencha-touch-debug.js 四個檔案也改一下

  • 要修改的地方:
    找到檔案 MyApp/.sencha/app/microloader/production.js,找到
addMeta('viewport', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no');

改為

addMeta('viewport', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover');

不嫌麻煩,可以把 microloader 下 development.js、testing.js 也改一下

如果是 ExtJS6 Modern

因為 sencha app build native/ios 構建(打包)的結果裡面有個 ios.json,這個是 應用的清單檔案(裡面包含了要載入的js和css檔案列表),如下圖:
這裡寫圖片描述

app在啟動時 index.html 內的引導程式碼,會先 ajax(XMLHttpRequest) 請求 ios.json,而後根據清單內容才載入 app.jsapp.css 等檔案。

但是,因為 WKWebview 的安全機制, index.html 無法請求 file:///..../www/ios.json,指令碼錯誤如下:

XMLHttpRequest cannot load file 'file:///..../www/ios.json'

我們之前不是加了 cordova-plugin-wkwebview-file-xhr 外掛,攔截 webview 請求,利用原生方式請求了嗎?為什麼還不行呢?這是因為 cordova.jsios.json 清單檔案中,也就是說 index.html 在載入 ios.json 的時候,cordova 外掛還沒有生效。

那麼,解決辦法就是,設法先載入 cordova.js,等外掛生效後,再來載入 ios.json 以及後面的 app.jsapp.css

修改步驟:
1) 在 index.html 的 <head></head> 中加入

<script type="text/javascript" src="cordova.js"></script>

注意放在 <script id="microloader" data-app="...." type="text/javascript" src="bootstrap.js"></script> 之前
如下:
這裡寫圖片描述

2) 將 cordova.js 從最終 build 後的 ios.json 清單檔案中移出
修改 MyApp\.sencha\app\app.defaults.json
把這段去掉:
這裡寫圖片描述
注:如果是 phonegap,應該刪除這裡
這裡寫圖片描述

這樣,build 之後的 ios.json 中就沒有 cordova.js 了。

3) 等 cordova 外掛生效後,才允許 index.html 載入 ios.json
修改 MyApp\.sencha\app\Boot.js,將原來的 fetch 函式改名為 _fetch,然後新加一個 fetch 函式:

// 新加一個 `fetch` 函式
fetch: function() {
    var me = this,
        args = arguments;
    if(Ext.platformTags.cordova) { // 如果是 cordova
        document.addEventListener('deviceready', function() { // 等 cordova 外掛生效後,才載入
            me._fetch.apply(me, args);
        }, false);
    }
    else {
        me._fetch.apply(me, args);
    }
},

// 原來的 `fetch` 函式改名為 `_fetch`
_fetch: function(url, complete, scope, async) {
    async = (async === undefined) ? !!complete : async;
    var xhr = new XMLHttpRequest(),
        result, status, content, exception = false,
        readyStateChange = function () {
            if (xhr && xhr.readyState == 4) {
                status = (xhr.status === 1223) ? 204 :

如下圖:
這裡寫圖片描述