Понимание вьюпорта WebView в iOS 11
Перевод статьи Understanding the WebView Viewport in iOS 11 с сайта ayogo.com для CSS-live.ru, автор — Дэррил Пог
iOS 11 представила новое, — пожалуй, неинтуитивное — поведение, связанное со статус-баром. Оно имеет особое значение для тех, кто разрабатывает приложения при помощи инструментов вроде Apache Cordova и Ionic. В частности, изменение затрагивает веб-приложения под iOS11, использующие фиксированное позиционирование верхней панели навигации. Эта статья поможет понять вьюпорт WebView в iOS 11.
Примечание: уже существующие приложения продолжат работать так, как всегда работали — без изменений в поведении их вьюпорта. Нововведение затрагивает только приложения, скомпилированные при помощи Xcode 9 и предназначенные для iOS 11.
Чтобы понять это изменение, посмотрим на его контекст.
Статус-бары и безопасные зоны
В ранних версиях iOS статус-бар был просто черной полосой через верхнюю часть экрана и обычно не реагировал на касания. Он был частью системного интерфейса, и приложение запускалось в пространстве под ним.
Это изменилось с выходом iOS 7, у которой был прозрачный статус-бар. Он принимал цвет верхней панели навигации приложения. Для приложений, отображающихся в WebView, наподобие Cordova, это часто означало определение версии iOS и добавление 20px
верхнего отступа к фиксированной верхней панели навигации. Таким образом, она вставала правильно.
Последующие версии iOS представили небольшие доработки. В их числе была такая особенность: во время звонка в статус-баре мог отображаться дополнительный баннер, а также когда приложение фоново использовало геолокацию.
В нативных приложениях это во многом автоматически управлялось стандартными средствами: UINavigation Bar (управление панелью навигации) и autolayout (авто-выравнивание). Были инструменты выравнивания верхней и нижней части экрана, которые автоматически подстраивали их под высоту статус-бара (-баров). Они проверяли, что контент приложения находится в безопасной зоне, и статус-бар его не перекроет. Если имелась панель навигации UINavigation Bar, выровненная по верхнему краю, то iOS автоматически добавляла ее цвет в качестве фона статус-бара. Для веба, к сожалению, не было такого эквивалента.
Изменения в iOS 11
Поведение вьюпорта по умолчанию в iOS 11 на iPhone 8
Отличие iOS 11 от более ранних версий состоит в том, что контент WebView теперь учитывает безопасные зоны. Это значит, что если имеется фиксированная панель навигации со свойством top: 0
, то она по умолчанию отрисуется на 20px
ниже верхнего края экрана — выровненная по нижнему краю статус-бара. При прокрутке вниз она сдвинется наверх, под статус-бар. При прокрутке вверх она снова выпадет прямо под статус-баром (оставляя нелепый зазор в 20px
, через который проглядывает контент).
Вы можете убедиться, насколько это плохо, в этом видео:
Очень странное поведение скролла в iOS 11 для элементов с фиксированным позиционированием
Почему вообще Apple внесли такое изменение?
Если вы видели дизайн iPhone X, то цель изменения понятна. У iPhone X нестандартная форма экрана с выемкой наверху для динамика и камеры. Если фиксированные элементы выровнять по реальному верхнему краю экрана, то они станут недоступными позади этой выемки.
Выравнивание по нижнему краю статус-бара гарантирует, что всё, что находится в панели навигации, будет доступным.
Круто… Не считая того, что теперь приложения выглядят ужасно со странной верхней панелью навигации, двигающейся вверх и вниз, и контентом, который проглядывает через статус-бар во время прокрутки.
Фиксы iOS 11
К счастью, Apple дали нам способ управлять этим поведением при помощи мета-тега viewport
. И даже больше — они портировали фикс нового поведения вьюпорта для прежнего, устаревшего UIWebView!
Свойство мета-тега viewport
, которое нам нужно, — это viewport-fit
. Оно имеет три возможных значения:
contain
: вьюпорт должен полностью вмещать веб-контент. Тогда фиксированные элементы будут содержаться внутри безопасной зоны на iOS 11.cover
: веб-контент должен полностью покрывать вьюпорт. Тогда фиксированные элементы будут зафиксированы во вьюпорте. Даже в тех случаях, когда это означает, что они будут перекрыты. Это восстанавливает поведение, которое было при iOS 10.auto
: значение по умолчанию, в этом случае поведение веб-контента такое же, как приcontain
.
Таким образом, чтобы вернуть панель навигации к самому верху экрана, за статус-бар, как это было на iOS 10, следует добавить в мета-тег viewport
свойство viewport-fit=cover
.
Всё в порядке в iOS 11 на iPhone 8, когда свойство viewport-fit
имеет значение cover
iPhone X
Но что насчет iPhone X с его нестандартной формой? Статус-бар больше не имеет высоту 20px
. Из-за выемки для камеры и динамика содержимое верхней панели навигации будет полностью недоступным для пользователей. Важно отметить, что это также относится и к нижней панели навигации, прикрепленной к низу экрана, которая будет перекрыта микрофоном.
Примечание: приложение использует все пространство экрана на iPhone X только в том случае, если у вас есть сториборд для экрана загрузки. Уже существующие приложения будут показаны в контейнере просмотра с черным пространством сверху и снизу.
На iPhone X уже появляется проблема, даже при том же значении cover
у свойства viewport-fit
К счастью, Apple добавили способ управлять выравниванием безопасной зоны через CSS. Они ввели новое понятие, похожее на CSS-переменные, изначально названное CSS-константами. Вы можете представить их как системно заданные CSS-переменные, которые нельзя переопределить. Они были предложены Рабочей группе CSS для стандартизации. Она приняла предложение, но с условием: для доступа к этим константам нужно будет использовать функцию с названием env()
вместо constant()
. (Изначально Apple предложили название constant()
— прим. переводчика.)
Примечание: iOS 11 использует синтаксис с constant()
, но последующие версии операционной системы будут поддерживать только env()
!
Четыре константы для управления раскладкой безопасных зон — это:
env(safe-area-inset-top)
: значение отступа безопасной зоны (в CSS-пикселах), считая от верхнего края вьюпорта.env(safe-area-inset-bottom)
: значение отступа безопасной зоны (в CSS-пикселах), считая от нижнего края вьюпорта.env(safe-area-inset-left)
: значение отступа безопасной зоны (в CSS-пикселах), считая от левого края вьюпорта.env(safe-area-inset-right)
: значение отступа безопасной зоны (в CSS-пикселах), считая от правого края вьюпорта.
И, в качестве последнего подарка для нас, Apple портировало эти переменные для UIWebView.
Пример с CSS-константами
Скажем, есть фиксированная верхняя панель навигации, и CSS для iOS 10 сейчас выглядит так:
header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 44px;
padding-top: 20px; /* Высота статус-бара */
}
Чтобы она автоматически подстраивалась под iPhone X и другие устройства на iOS 11, надо добавить свойство viewport-fit=cover
в мета-тег viewport
и изменить CSS с обращением к константе:
header {
/* ... */
/* Высота статус-бара в iOS 10 */
padding-top: 20px;
/* Высота статус-бара в iOS 11.0 */
padding-top: constant(safe-area-inset-top);
/* Высота статус-бара в iOS 11+ */
padding-top: env(safe-area-inset-top);
}
Важно, чтобы для старых устройств, не понимающих синтаксис constant()
или env()
, осталось страховочное значение. Можно также использовать константы в CSS-функции calc()
.
Проблема на iPhone X решена через добавление отступа в зависимости от устройства
Также не стоит забывать делать то же самое для нижней панели навигации.
Особое спасибо Тимоти Хортону из команды WebKit в Apple за внедрение функций constant()/env()
, которые мы обсуждали в этой статье. Спасибо Шазрон, Хулио, Керри, Грегу и Майку за помощь в тестировании и проверке некоторых из описанных в статье поведений.
P.S. Это тоже может быть интересно:
Вначале придумать, проблему, а затем ее решить. (И получить как с градиентом двойной синтаксис)
Тогда нужно и под выпуклость экрана тоже ключевые слова предусмотреть, чтобы потом не было новой sypercontant(safe-area-outside-top).
viewport-fit=cover не работает на ios 13