Спасибо @pascalzon и @YYL за полезную информацию. Я попытался переключиться на WKWebView для своего приложения Cordova, но оно просто сломало его, поэтому на данный момент я застрял с uiWebview и, таким образом, с этой проблемой.
Я хочу, чтобы мое приложение выглядело хорошо на устройствах с надрезом, таких как iPhone X, поэтому я добавил viewport-fit=cover
в объявление области просмотра приложения и начал играть с безопасными вставками. Мои макеты достаточно просты. Верхнее поле должно быть 1rem или safe-area-inset-top, если значение больше 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
оттуда каждый раз, когда изменялась ориентация экрана.
Поскольку, похоже, пока что нет способа чтения переменных env CSS из javascript, я начинаю с 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/
Единственный раз, когда вы можете полагаться на правильность значений вставки безопасной области, это при запуске приложения, поэтому я записываю их в набор переменных javascript внутри моего app
объекта:
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 в пиксели, умножая их на базовый размер шрифта.
Затем я конвертирую marginTopDefault
и newMarginTop
в целые числа, нахожу максимальное значение и задаю --app-margin-top
для этого.
$(":root").css("--app-margin-top", Math.max(marginTopDefault, newMarginTop) + "px");
Заключение
Я думаю, что описал это достаточно хорошо. Было очень сложно найти что-то работающее, что должно было сработать, но эй. Вот так иногда. И я не гуру HTML5, так что, возможно, есть способы, которыми это можно упростить.