[Cordova] 移动App 的 ios11 和 iPhoneX 适配

原文链接:https://blog.csdn.net/lovelyelfpop/article/details/79460700

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">

不过我测试的结果和上图一样,仍然有黑边有白边
这里写图片描述

更新启动图

Google 后得知,需要更新启动图为 Launch Storyboard Image,具体参考 iOS 启动图 (launch storyboard images) 规范

<platform name="ios">
  <splash src="res/screen/ios/Default@2x~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 :

如下图:
这里写图片描述

相关文章
相关标签/搜索