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

时间:2018-11-13 22:02:40

标签: ios cordova layout

我在Cordova应用中使用CSS变量safe-area-inset-top处理iPhone X安全区域:

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

在应用启动时按预期工作。但是,当我通过自定义插件进入和退出全屏(强制横向)AVPlayer并返回到肖像应用程序时,填充区消失了,我的应用程序被部分切断了。

我目前正在iOS上的插件中的AVPlayerViewController类中使用此功能。

LandscapeVideo.m

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

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}

在此先感谢您的帮助/想法!

3 个答案:

答案 0 :(得分:2)

如关于此答案的设备方向的注释所示: https://stackoverflow.com/a/46232813/1085272

使用uiWebView(默认设置)后,某些方向发生更改后,出现安全区域插入xxx问题。

切换到WKWebView(例如,使用cordova-plugin-wkwebview-engine)为我解决了该问题。

答案 1 :(得分:2)

修复了iPhone X / XS屏幕旋转问题

在iPhone X / XS上,屏幕旋转将导致标题栏高度使用不正确的值,因为safe-area-inset- *的计算未及时反映UI刷新的新值。即使在最新的iOS 12中,UIWebView中也存在此错误。一种解决方法是插入一个1px的上边距,然后快速将其反转,这将触发安全区域插入*立即被重新计算。修复起来有些丑陋,但是如果您出于某种原因而不得不使用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);

代码的目的是使document.body.style.marginTop稍作更改,然后将其反转。不一定必须是“ 1px”。您可以选择一个不会导致UI闪烁但可以达到其目的的值。

答案 2 :(得分:0)

感谢@pascalzon和@YYL提供有用的信息。我尝试为我的Cordova应用程序切换到WKWebView,但它只是中断了它,所以暂时我一直被uiWebview所困扰,因此也遇到了这个问题。

我希望我的应用程序在类似iPhone X的缺口设备上看起来不错,因此我在应用程序的视口声明中添加了viewport-fit=cover,并开始使用安全区域插入功能。我的布局需求非常简单。如果上边距的值大于1rem,则必须为1rem或安全区域插入顶部。

不幸的是,CSS max()函数还不是标准功能。如果我可以使用的话,事情会更简单(假设它将所有内容转换为像素)。

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

因此,我不得不在javascript中执行最大比较,然后每次屏幕方向更改时都从此处设置--app-margin-top

由于到目前为止,似乎还没有办法从javascript中读取CSS env vars,因此我开始在CSS中记录CSS变量中的安全区域插图,以便以后从javascript中进行访问。默认值为:

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

然后我将这些变量设置如下:

/* 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);
    }
}

每次屏幕方向改变时,我都会将--safe-area-inset-top--app-margin-top-default进行比较,并将--app-margin-top设置为这些值的最大值。那时我遇到了这个问题。屏幕方向更改和我的CSS安全区域插入变量被更新之间似乎存在滞后。因此我的边距设置代码无效。

我尝试了YYL的技巧来强制重新计算安全区域插图,但是似乎没有用。插图最终进行了更新,但是在我的屏幕方向更改事件侦听器完成执行:(

我的解决方案

此解决方案依赖于cordova屏幕方向插件:

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

唯一可以依靠正确的安全区域插入值的时间是在应用程序启动时,因此我将它们记录在app对象内部的一组javascript变量中:

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

在启动时设置这些变量的方法会考虑屏幕方向。

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!";
}

在启动时,然后每次屏幕方向改变时,我都会再次检查是否需要像这样更新--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是一个辅助函数,通过将rems乘以基本字体大小将rems转换为像素。

然后我将marginTopDefaultnewMarginTop转换为整数,找到最大值并将--app-margin-top设置为此。

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

结论

我想我已经描述得足够好了。要使一些本来可以工作的工作变得很混乱,但是嘿。有时候就是这样。而且我不是HTML5专家,所以可能有一些方法可以使之更简单。