原文链接:https://blog.csdn.net/lovelyelfpop/article/details/79460700
Apple每次退出新尺寸的iphone都会掀起一番适配风波,这次没有下巴但有刘海的iPhoneX更是如此,网传横屏下的适配动画更是令不少人汗颜。
其实对于Native App来说,适配并不算困难(当然追求酷炫效果另算),官方文档有详细的说明,而对于Web App来说,主要还是依靠打开webview的Native App来适配,而这篇文章主要讨论的是Cordova App要如何适配iPhoneX.
1、以前上架的应用,目标编译平台ios10,上面的刘海和下面的横杆自动被黑边覆盖
每次大版本更新和适配,首先都是更新 Cordova 创建,确认使用的 cordova 插件有是否有新的改进, 例如 cordova-plugin-splashscreen, cordova-plugin-statusbar 等
meta 标签中 添加 viewport-fit=cover,这是 ios 11 新增的设置,可以让页面全屏展示。
Google 后得知,需要更新启动图为 Launch Storyboard Image,具体参考 iOS 启动图 (launch storyboard images) 规范
更换 webview 只要添加 cordova-plugin-wkwebview-engine 插件
(下述删除线内容废弃,最新版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;
更换完 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 请求,需要增加配置:
真机上,图片又显示不出来了。
本来以为是图片下载失败,调试发现,图片已经下载下来了,但是 WKWebview 显示不出已经下载好的本地图片。
然而,模拟器确是正常的。
Google 了一大圈,发现如果图片文件存在的是 Tmp 目录下,也就是 file:///var/mobile/Applications/Container/Data/
目录,则真机上可以显示图片,其他目录就不行。
那就换成 Tmp 目录。
注:Cordova 可以使用 cordova.file.tempDirectory
这个常量来得到 Tmp 目录的具体路径。
Safe-Area,安全区域是 iPhone X 畸形全面屏的产物。虽说是全面屏,但是顶部多了传感器(刘海)和底部 home 键横线,为了让你的应用在 iPhone X 上 面完美运行,你需要将可视元素扩展至填充整个展示窗口(屏幕)上,同时,你也需要保证如按钮、tab bar 等可交互控件,以及一些至关重要的信息不会因为屏幕的圆角而被裁掉,或者被手机的「刘海」和虚拟「Home键」遮住。
利用 苹果提供的 CSS 常量 env(safe-area-inset-*)
,可以将重要的界面元素避开刘海、圆角和横线。
Safe-Area 的适配,详细方法自己百度。
框架会对 index.html 页面添加2次
所以我们无需在 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 也改一下
因为 sencha app build native/ios
构建(打包)的结果里面有个 ios.json
,这个是 应用的清单文件(里面包含了要加载的js和css文件列表),如下图:
app在启动时 index.html
内的引导代码,会先 ajax(XMLHttpRequest) 请求 ios.json
,而后根据清单内容才加载 app.js
和 app.css
等文件。
但是,因为 WKWebview 的安全机制, index.html
无法请求 file:///..../www/ios.json
,脚本错误如下:
XMLHttpRequest cannot load file 'file:///..../www/ios.json'
我们之前不是加了 cordova-plugin-wkwebview-file-xhr
插件,拦截 webview 请求,利用原生方式请求了吗?为什么还不行呢?这是因为 cordova.js
在 ios.json
清单文件中,也就是说 index.html
在加载 ios.json
的时候,cordova 插件还没有生效。
那么,解决办法就是,设法先加载 cordova.js
,等插件生效后,再来加载 ios.json
以及后面的 app.js
、app.css
。
修改步骤:
中加入
cordova.js
从最终 build 后的 ios.json
清单文件中移出MyApp\.sencha\app\app.defaults.json
这样,build 之后的 ios.json
中就没有 cordova.js
了。
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 :