简体   繁体   English

布局/方向改变后的Cordova iPhone X安全区域。

[英]Cordova iPhone X safe area after layout/orientation changes.

I'm using the CSS variable safe-area-inset-top in my Cordova app to handle iPhone X safe area: 我在Cordova应用程序中使用CSS变量safe-area-inset-top来处理iPhone X安全区域:

body {
 padding-top: env(safe-area-inset-top);
}

Works as expected when the app boots up. 应用程序启动时按预期工作。 However, when I enter and exit a full screen (forced landscape) AVPlayer via a custom plugin, and return to the portrait app, the padding is gone and my app is partially cut off. 但是,当我通过自定义插件进入和退出全屏(强制横向)AVPlayer,并返回到纵向应用程序时,填充消失,我的应用程序被部分切断。

I'm currently using this in my AVPlayerViewController class within the plugin on iOS. 我目前在iOS上的插件中的AVPlayerViewController类中使用它。

LandscapeVideo.m LandscapeVideo.m

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight; // or LandscapeLeft
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}

Thanks in advance for any help / ideas! 在此先感谢任何帮助/想法!

Fix for iPhone X/XS screen rotation issue 修复iPhone X / XS屏幕旋转问题

On iPhone X/XS, a screen rotation will cause the header bar height to use an incorrect value, because the calculation of safe-area-inset-* was not reflecting the new values in time for UI refresh. 在iPhone X / XS上,屏幕旋转将导致标题栏高度使用不正确的值,因为safe-area-inset- *的计算未及时反映UI刷新的新值。 This bug exists in UIWebView even in the latest iOS 12. A workaround is inserting a 1px top margin and then quickly reversing it, which will trigger safe-area-inset-* to be re-calculated immediately. UIWebView中存在此错误,即使在最新的iOS 12中也是如此。解决方法是插入1px上边距然后快速反转,这将触发安全区域插入*立即重新计算。 A somewhat ugly fix but it works if you have to stay with UIWebView for one reason or another. 一个有点丑陋的修复,但如果您因为某种原因不得不继续使用UIWebView,它会起作用。

 window.addEventListener("orientationchange", function() { var originalMarginTop = document.body.style.marginTop; document.body.style.marginTop = "1px"; setTimeout(function () { document.body.style.marginTop = originalMarginTop; }, 100); }, false); 

The purpose of the code is to cause the document.body.style.marginTop to change slightly and then reverse it. 代码的目的是使document.body.style.marginTop稍微改变然后反转它。 It doesn't necessarily have to be "1px". 它不一定是“1px”。 You can pick a value that doesn't cause your UI to flicker but achieves its purpose. 您可以选择一个不会导致UI闪烁但实现其目的的值。

As seen in the notes about device orientation of this answer: https://stackoverflow.com/a/46232813/1085272 如有关此答案的设备方向的说明中所示: https//stackoverflow.com/a/46232813/1085272

There is a safe-area-inset-xxx issue after some orientation changes when using uiWebView (which is default). 使用uiWebView(默认情况下)后,在某些方向更改后会出现安全区域插入xxx问题。

Switching to WKWebView (eg by using cordova-plugin-wkwebview-engine ) fixed the issue for me. 切换到WKWebView(例如使用cordova-plugin-wkwebview-engine )为我解决了这个问题。

Thanks to both @pascalzon and @YYL for useful information. 感谢@pascalzon和@YYL获取有用的信息。 I tried switching to WKWebView for my Cordova app but it just broke it, so for the time being I am stuck with uiWebview and thus this issue. 我尝试切换到我的Cordova应用程序的WKWebView,但它只是打破了它,所以暂时我被困在uiWebview,因此这个问题。

I want my app to look nice on notched devices like the iPhone X so I added viewport-fit=cover to the app's viewport declaration and started playing with safe-area-insets. 我希望我的应用程序在iPhone X等缺口设备上看起来不错,所以我在应用程序的视口声明中添加了viewport-fit=cover并开始使用安全区域插件。 My layout needs are quite simple. 我的布局需求非常简单。 The top margin must either be 1rem or safe-area-inset-top if this has a value greater than 1rem. 如果此值大于1rem,则上边距必须为1rem或safe-area-inset-top。

Unfortunately, the CSS max() function is not yet standard. 不幸的是,CSS max()函数还不是标准的。 If I could have used that things would have been simpler (assuming it converts everything to pixels). 如果我可以使用它会更简单(假设它将所有内容转换为像素)。

:root {
    --app-margin-top-default: 1rem;
    --app-margin-top: max(env(safe-area-inset-top), var(--app-margin-top-default));
}

So I was forced to perform the max comparison in javascript and then set --app-margin-top from there each time the screen orientation changed. 所以我被迫在javascript中执行最大比较,然后在每次屏幕方向改变时从那里设置--app-margin-top

Since there seems to be no way, as yet, of reading CSS env vars from javascript, I start in the CSS by recording safe area insets in CSS variables which I can access later from javascript. 由于似乎没有办法从javascript中读取CSS env变种,我从CSS中开始记录CSS变量中的安全区域插入,我可以稍后从javascript访问。 Default values are: 默认值为:

:root {
    --safe-area-inset-top:      0px;
    --safe-area-inset-right:    0px;
    --safe-area-inset-bottom:   0px;
    --safe-area-inset-left:     0px;
}

I then set these vars as follows: 然后我按如下方式设置这些变量:

/* iOS 11.0: supports constant() css function. (Assume all other inset vars are supported.) */
@supports (padding-top: constant(safe-area-inset-top))  {
    :root {
        --safe-area-inset-top:      constant(safe-area-inset-top, 0);
        --safe-area-inset-right:    constant(safe-area-inset-right, 0);
        --safe-area-inset-bottom:   constant(safe-area-inset-bottom, 0);
        --safe-area-inset-left:     constant(safe-area-inset-left, 0);
    }
}
/* iOS 11.2 and latest Chrome webviews support the env() css function. (Assume all other inset vars are supported.) */
@supports (padding-top: env(safe-area-inset-top))  {
    :root {
        --safe-area-inset-top:      env(safe-area-inset-top, 0);
        --safe-area-inset-right:    env(safe-area-inset-right, 0);
        --safe-area-inset-bottom:   env(safe-area-inset-bottom, 0);
        --safe-area-inset-left:     env(safe-area-inset-left, 0);
    }
}

Each time the screen orientation changed I would then compare --safe-area-inset-top with --app-margin-top-default and set --app-margin-top to the maximum of these values. 每次屏幕方向改变时,我会比较--safe-area-inset-top--app-margin-top-default和set --app-margin-top到这些值的最大值。 It was then that I ran into this problem. 就在那时我遇到了这个问题。 There seemed to be a lag between the screen orientation changing and my CSS safe area inset variables being updated. 屏幕方向更改与我的CSS安全区域插入变量之间似乎存在延迟。 So my margin setting code did not work. 所以我的保证金设定代码不起作用。

I tried YYL's trick to force recalculation of the safe area insets but it didn't seem to work. 我试过YYL的伎俩强制重新计算安全区域插图,但它似乎没有用。 The insets were eventually updated but after my screen orientation change event listener had finished executing :( 插件最终更新但在我的屏幕方向更改后事件监听器已完成执行:(

My Solution 我的解决方案

This solution relies on the cordova screen orientation plugin: 此解决方案依赖于cordova屏幕方向插件:

https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-screen-orientation/ https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-screen-orientation/

The only time you can rely on the safe area inset values being right is at application start up, so I record them in a set of javascript variables inside my app object: 您可以依赖安全区域插入值正确的唯一时间是在应用程序启动时,所以我将它们记录在我的app对象中的一组javascript变量中:

safeAreaInsetNorth: undefined,
safeAreaInsetEast: undefined,
safeAreaInsetSouth: undefined,
safeAreaInsetWest: undefined,

The method that sets these variables at startup takes screen orientation into account. 在启动时设置这些变量的方法需要考虑屏幕方向。

console.log("Screen orientation at startup is: " +screen.orientation.type);
let $root = $(":root");
// Notch/North at the top.
if (screen.orientation.type.match("portrait-primary")) {
    app.safeAreaInsetNorth = $root.css("--safe-area-inset-top");
    app.safeAreaInsetEast  = $root.css("--safe-area-inset-right");
    app.safeAreaInsetSouth = $root.css("--safe-area-inset-bottom");
    app.safeAreaInsetWest  = $root.css("--safe-area-inset-left");
}
// Upside down... Notch/North at the bottom.
else if (screen.orientation.type.match("portrait-secondary")) {
    app.safeAreaInsetNorth = $root.css("--safe-area-inset-bottom");
    app.safeAreaInsetEast  = $root.css("--safe-area-inset-left");
    app.safeAreaInsetSouth = $root.css("--safe-area-inset-top");
    app.safeAreaInsetWest  = $root.css("--safe-area-inset-right");
}
// Notch/North to the left.
else if (screen.orientation.type.match("landscape-primary")) {
    app.safeAreaInsetNorth = $root.css("--safe-area-inset-left");
    app.safeAreaInsetEast  = $root.css("--safe-area-inset-top");
    app.safeAreaInsetSouth = $root.css("--safe-area-inset-right");
    app.safeAreaInsetWest  = $root.css("--safe-area-inset-bottom");
}
// Notch/North to the right.
else if (screen.orientation.type.match("landscape-secondary")) {
    app.safeAreaInsetNorth = $root.css("--safe-area-inset-right");
    app.safeAreaInsetEast  = $root.css("--safe-area-inset-bottom");
    app.safeAreaInsetSouth = $root.css("--safe-area-inset-left");
    app.safeAreaInsetWest  = $root.css("--safe-area-inset-top");
} else {
    throw "I have no idea which way up I am!";
}

At startup and then again each time the screen orientation changes I check to see if I need to update --app-margin-top like this: 在启动时,每次屏幕方向改变时,我都会检查是否需要更新--app-margin-top如下所示:

let marginTopDefault = app.getGlobalCssVariableInPx("--app-margin-top-default");
let newMarginTop = undefined;
switch(screen.orientation.type) {
    case "portrait-primary": // Notch/North at the top.
        newMarginTop = app.safeAreaInsetNorth;
        break;
    case "portrait-secondary": // Upside down... Notch/North at the bottom.
        newMarginTop = app.safeAreaInsetSouth;
        break;
    case "landscape-primary": // Notch/North to the left.
        newMarginTop = app.safeAreaInsetEast;
        break;
    case "landscape-secondary": // Notch/North to the right.
        newMarginTop = app.safeAreaInsetWest;
        break;
    default:
        throw "No idea which way up I am!";
}

app.getGlobalCssVariableInPx is a helper function that converts rems into pixels by multiplying them by the base font size. app.getGlobalCssVariableInPx是一个辅助函数,它通过将rems乘以基本字体大小将rems转换为像素。

I then convert marginTopDefault and newMarginTop to integers, find the max and set --app-margin-top to this. 然后我将marginTopDefaultnewMarginTop转换为整数,找到max并设置--app-margin-top

    $(":root").css("--app-margin-top", Math.max(marginTopDefault, newMarginTop) + "px");

Conclusion 结论

I think I have described this well enough. 我想我已经很好地描述了这一点。 It was a lot of messing about to get something working that should have just worked out of the box, but hey ho. 得到一些应该刚刚开箱即用的工作是很麻烦的,但是嘿嘿。 That's how it goes sometimes. 这有时是怎么回事。 And I am no HTML5 guru, so there are probably ways in which this could made simpler. 而且我不是HTML5大师,因此可能会有更简单的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM