Supporting iPhone X for mobile web & Cordova app using Onsen UI
iPhone X is the latest model of Apple’s iPhone series unveiled in September 2017. This unique & eloquent handset will be sold worldwide from November 3rd.
At the first glance you will notice this handset, especially its screen, is a total re-shape from the existing iPhones.
- It has a “notch” on the top.
- Rounded corners.
- A black bar is always present on the bottom.
These changes will affect all UI design and implements including Web, Cordova and native apps. Therefore, developers have to deal with changes/adaptations specifically for iPhone X. In this article, I will talk about necessary changes needed for iPhone X.
Along the way I’ll mention how to create Vue apps like native iPhone X apps using Onsen UI, which has recently supported iPhone X.
TL;DR
- If you see left/right white blank areas in your web or Cordova apps in landscape mode of iPhone X Safari, you can remove them by setting
viewport-fit=cover
orbody { background-color: black; }
. - If the notch, the rounded corners or the bottom bar of iPhone X conflict with page contents in your web or Cordova apps, you should set appropriate
margin
andpadding
to outermost boxes. (If you are using Onsen UI, it can be done by attachingonsflag-iphonex-portrait
andonsflag-iphonex-landscape
attributes tohtml
element.) - If you see black blank areas in your Cordova app on iPhone X, you can remove them by enabling storyboard-based splash screen using
cordova-plugin-splashscreen
or providing an iPhone X-size (2436 x 1242 px) splash screen image. - The source code of the example is available at this GitHub repo.
This article is divided into two main parts:
- PART I: Creating Native-like Web Apps for iPhone X
- PART II: Creating Native-like Apps with Cordova for iPhone X
PART I — Creating Native-like Web Apps for iPhone X
In the case of web apps, supporting iPhone X means supporting iPhone X Safari.
But what happens if we show web apps in iPhone X safari?
Set up a web app
First of all, let’s create a web app. For smoother explanation, I’m going to implement Tab bar pattern, the most common pattern in iOS apps.
I chose Onsen UI for its implementation. Onsen UI is an open source library containing a set of iOS and Android components for Vue apps.
The versions of NPM packages which we’ll use are shown below:
vue@2.5.2
onsenui@2.7.0
(Core package)vue-onsenui@2.3.0
(Additional package for Vue)
(1) For existing Vue projects, Onsen UI can be installed with NPM or Yarn:
# NPM
npm install onsenui vue-onsenui --save-dev# Yarn
yarn add onsenui vue-onsenui -D
Some necessary files must be included in the app:
import 'onsenui/css/onsenui.css'; // Webpack CSS import
import 'onsenui/css/onsen-css-components.css'; // Webpack CSS importimport VueOnsen from 'vue-onsenui';
Vue.use(VueOnsen);
(2) Otherwise, new projects can be started right away via Vue CLI. It can optionally add Vuex and some other features:
vue init OnsenUI/vue-pwa-webpack # For PWA
Once Vue and Onsen UI are set up, the next step is to create App.vue
and NotesPage.vue
with the following codes and get the tab bar pattern implemented.
That’s it.
I don’t explain much about how to implement the tab bar pattern in this post. If you’d like to know more about it, please refer to the v-ons-tabbar
section in the Onsen UI docs.
Problem I —Left/Right Blank Area in Landscape Mode
So, let’s show the created tab bar pattern in iPhone X Safari. Now I’ll use the iPhone X Simulator available on Xcode 9 instead of the real one.
Launching a local HTTP server and accessing it from the simulator will show the following screens:
It seems fine in the case of portrait mode. Then, let’s see what happens if we rotate the screen.
Oh, white blank areas appeared on the both side. Actually they are automatically inserted by iPhone X Safari. This is because if there are no blank areas on both side, the notch and the rounded corners will hide some of the page contents.
I’ll call it Problem I — Left/Right blank areas appearing in landscape mode.
The color of these left/right blank areas can be changed by specifying background-color
on the html
element and body
element, so if we set it to black
some of the strangeness can be reduced.
body {
background-color: black; /* Quick fix for Problem I */
}
But this solution causes a mismatch between the width of the address bar and that of the page in non-black colored pages and results in a weird looking, so the solution is not a complete one.
Fix Problem I — Controlling the Left/Right Blank Areas
iPhone X Safari provides a feature to enable/disable the left/right blank areas.
The blank areas can be controlled by utilizing a special argument viewport-fit
in <meta name="viewport" content="...">
. viewport-fit
accepts auto
, contain
, and cover
as valid values.
auto
Default value. Equivalent tocontain
in Safari.contain
The viewport does not cover the whole screen, creating the blank areas.
(Similar to the behavior ofobject-fit: contain;
)cover
The viewport covers the whole screen, removing the blank areas.
So let’s set viewport-fit
to cover
.
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
The blank areas have gone and Problem I has been fixed.
Problem II — Page Contents Hidden by the Obstacles
There are more problems other than Problem I. You would notice that the bottom black bar is hiding the text part of the tab bar. Moreover, by solving Problem I, the notch and the rounded corners have hidden more page contents.
Problem II is that the notch, the rounded corners and the bottom bar can hide some part of the page contents.
Fix Problem II
Basically we can fix the problem that page components are hidden by setting appropriate values to margin
and padding
in order to prevent boxes are hidden by the notch, the corners and the bar.
As the easiest solution, we can also set margin-left
and margin-right
to the body
element. Depending on the design of your web app, it might be enough. But if there is a box like tab bars which expands from the left side to the right side, setting margin to the body
element makes the box discontinue and causes a weird look. In addition, we have to deal with the problem that texts are hidden by the black bottom bar.
Hence, depending on the design of your web app, you have to set margin
and padding
to all boxes causing the problem. It’s quite tedious, but it is also necessary to achieve an optimized UI for iPhone X Safari.
If we want to set margin
and padding
to a particular box, we need to make the setting enabled only in landscape mode.
In order to achieve it, orientation
media feature can be used. When(orientation: landscape)
is added to a media query, the corresponding style sheet will be active only if the viewport is in landscape mode (the dimension satisfies width
> height
).
@media screen and (orientation: landscape) {
.some-box {
padding-left: 44px;
padding-right: 44px;
}
}
By the way, how far we should locate boxes from the edge of the screen to prevent the boxes hidden by the notch, the corners and the bar?
As the concrete values, Apple has defined a concept, called safe area.
Understanding Safe Area
Safe area is a region, where the notch, the corners and the bar cannot reach.
All apps should adhere to the safe area and layout margins defined by UIKit, which ensure appropriate insetting based on the device and context.
(Source: https://developer.apple.com/ios/human-interface-guidelines/overview/iphone-x/)
Therefore if we want to locate boxes far from the edges of the screen, we just need to set the safe area insets, which is a set of lengths from each edge of the screen.
iPhone X Safari has “top=0, right=44, bottom=21, left=44” as its safe area insets in landscape mode. (We can check the value by using API of iOS.) So we just need to modify the tab bar to set the left/right padding to 44px
and the bottom padding to 21px
in our case.
Problem II can be fixed by such steady adjustment.
Tip: Use of
constant()
requires attentionThe official WebKit blog explains how to support safe area with a new CSS function
constant()
and four constantssafe-area-inset-left
,safe-area-inset-right
,safe-area-inset-top
,safe-area-inset-bottom
in a post published on Sep 22, 2017. The four constantssafe-area-inset-*
have two nice characteristics: “the value is set only ifviewport-fit
is set tocover
“ and “the value is automatically switched between the portrait value and the landscape value if the screen rotates.”But
constant()
function and the four constantssafe-area-inset-*
have the following problems:(1)
constant()
function has been removed from the CSS standard and renamed toenv()
function as of Sep 21, 2017.constant()
function is still available on iOS 11.0 Safari, but it might be removed and become unusable in the future.(2)
constant()
andenv()
don’t work in browsers except for Safari as of Nov, 2017, and it causes errors if we write codes such aspadding-left: calc(constant(safe-area-inset-left) + 10px);
. So we always have to write fallback rules for those browsers. It is a bit hard to use.(3) iOS 11.0 UIWebView has a bug that even if the screen rotates the change of the four constants
safe-area-inset-*
does not reflect the screen.I recommend using raw values (Example: top=0, right=44, bottom=21, left=44) directly or via variables instead of depending on
safe-area-inset-*
for some time to avoid the risk explained above.
Using iPhone X Support Helper by UI Libraries
Some UI libraries have a feature which helps iPhone X support. Onsen UI which I chose at this time provides a feature to automatically set appropriate margin
and padding
.
Adding onsflag-iphonex-landscape
attribute to the html
element enables a patch CSS for iPhone X and it changes margin
and padding
of each box which needs the fix.
onsflag-iphonex-landscape
attribute to the html
elementConsidering other iOS devices, theonsflag-iphonex-landscape
attribute should be added only in iPhone X. To do that, it would be easier to use this.$ons.platform.isIPhoneX()
:
beforeMount() {
const html = document.documentElement;
if (this.$ons.platform.isIPhoneX()) {
html.setAttribute('onsflag-iphonex-landscape', '');
}
},
Some UI libraries make it easy to solve the problem of setting margin
and padding
as I wrote above.
As well as Problem I, finally Problem II, which is the problem that page contents are hidden by the obstacles, has been fixed.
Summary of PART I
In PART I, we used the tab bar pattern as an example and explained Problem I (blank area problem) and Problem II (hiding page contents problem) followed by solutions for each problem.
By fixing Problem I and Problem II, iPhone X supports for Web apps have been solved.
In PART I, we mentioned the case of web apps.
But I think some of you are creating Cordova apps with the Cordova toolkit or Monaca. So I’d like to explain how to make your Cordova app compatible with iPhone X.
PART II — Creating Native-like Apps with Cordova for iPhone X
In the case of Cordova app, supporting iPhone X means supporting iPhone X WebView.
So let’s see what happens if we show Cordova apps in iPhone X WebView in the same way as PART I.
Set up a Cordova app
Let’s create a Cordova app with Vue.
The versions of software which we’ll use are shown below:
vue@2.5.2
onsenui@2.7.0
(Core package)vue-onsenui@2.3.0
(Additional package for Vue)cordova@7.1.0
cordova-ios@4.5.3
- Xcode 9.0
(1) For existing Cordova + Vue projects, it can be installed with NPM or Yarn:
# NPM
npm install onsenui vue-onsenui --save-dev# Yarn
yarn add onsenui vue-onsenui -D
Some necessary files must be included in the app:
import 'onsenui/css/onsenui.css'; // Webpack CSS import
import 'onsenui/css/onsen-css-components.css'; // Webpack CSS importimport VueOnsen from 'vue-onsenui';
Vue.use(VueOnsen);
(2) Otherwise, new projects can be started right away via Vue CLI. It can optionally add Vuex and some other features:
vue init OnsenUI/vue-cordova-webpack # For Cordova apps
(3) After setting up Cordova, Vue and OnsenUI, let’s create App.vue
and NotesPage.vue
with the following content as same as PART I and get the tab bar pattern implemented.
That’s it.
How about Problem I?
In PART I, we explained Problem I — white blank areas are automatically inserted on the both side by iPhone X Safari.
So let’s check if Problem I remains or not in iPhone X WebView.
cordova platform add ios
npm run build && cordova run ios --target="iPhone-X"
Oh, in the case of Cordova apps, it seems that the blank areas appear even in portrait mode.
Then, what happens in landscape mode?
Blank areas appeared on the left, right and bottom side in landscape mode.
It turned out that Problem I still happens in Cordova apps. But as same as PART I, this problem can be fixed by just setting viewport-fit
to cover
.
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
Tip: Problem I is already fixed in WKWebView
iOS 11 has two WebViews: an old UIWebView (Cordova default, and not recommended by Apple) and a new WKWebView (recommended by Apple).
In UIWebView, because
auto
of theviewport-fit
points tocontain
, we have to change it tocover
. But in WKWebView,auto
points tocover
by default (confirmed by the author). It means that Problem I is already fixed in WKWebView and we no longer have to setviewport-fit=cover
.As WKWebView is a next-generation API, basically it works stably compared to UIWebView in several situations. If you are using Cordova, I recommend replacing UIWebView with WKWebView by using
cordova-plugin-wkwebview-engine
plugin. But please also keep in mind that WKWebView has some minor drawbacks.
Problem III
In the case of web apps, once Problem I is fixed, we can start working on Problem II — the notch, the rounded corners and the bottom bar can hide some part of the page contents.
But in the case of Cordova apps, even if Problem I is fixed we cannot work on Problem II right away, because there is another problem, Problem III.
Let’s see what is happening in the whole screen after Problem I is solved.
There are black blank areas, in the top/bottom in portrait mode, and in the left/right and bottom in landscape mode. This is what I call Problem III.
Similar to Problem I, but the principle behind the problem is different. Problem III is caused by the specification of iPhone X that all apps which don’t satisfy a particular condition are automatically shown in the safe area. The condition is explained as follows:
Enable full screen native resolution. Your app will run in Full Screen Display Mode on iPhone X if your project’s base SDK is set to iOS 11 and you have a Launch Storyboard or iPhone X launch image.
(Source: https://developer.apple.com/ios/update-apps-for-iphone-x/ )
I’d like to explain the quoted part more specifically. First of all, iOS has two methods to show splash screens: (1) an old method which simply shows an image (Cordova default, not recommended by Apple) and (2) a new method which shows an image with some stretching and trimming based on a mechanism called storyboard (recommended by Apple).
In the case of (1), if the Cordova app does not contain an image which size is same as the iPhone X screen resolution (1125 x 2436 px), the app will be shown in the safe area. Adding the image with 1125 x 2436 px size fixes this problem and the app will be shown in full screen.
In the case of (2), all Cordova apps will be shown in full screen.
But in order to use (2), we have to add cordova-plugin-splashscreen
plugin into the Cordova app using the following command:
cordova plugin add cordova-plugin-splashscreen
And also the images which the splash screen consists of must be defined in <platform name="ios">
of config.xml
:
<splash src="res/screen/ios/Default@2x~iphone~anyany.png" />
<splash src="res/screen/ios/Default@2x~iphone~comany.png" />
<splash src="res/screen/ios/Default@2x~iphone~comcom.png" />
<splash src="res/screen/ios/Default@3x~iphone~anyany.png" />
<splash src="res/screen/ios/Default@3x~iphone~anycom.png" />
<splash src="res/screen/ios/Default@3x~iphone~comany.png" />
<splash src="res/screen/ios/Default@2x~ipad~anyany.png" />
<splash src="res/screen/ios/Default@2x~ipad~comany.png" />
Of course the actual image files must be added. The size of each image should basically follow the document of cordova-plugin-splashscreen
, and only for devices with Super Retina screen (@3x
) which has 3x resolution, I recommend setting the image size to 2436 x 1242 px so that it covers the longest long side of iPhone X (2436px) and the longest short side of iPhone 8 Plus (1242px). Please note that in the case of (2), apps are always shown in full screen and the size of each image only affects the behavior of the splash screen and does not affect the size of the app.
Default@2x~iphone~anyany.png (= 1334x1334 = 667x667@2x)
Default@2x~iphone~comany.png (= 750x1334 = 375x667@2x)
Default@2x~iphone~comcom.png (= 750x750 = 375x375@2x)Default@3x~iphone~anyany.png (= 2436x2436 = 812x812@3x)
Default@3x~iphone~anycom.png (= 2436x1242 = 812x414@3x)
Default@3x~iphone~comany.png (= 1242x2436 = 414x812@3x)Default@2x~ipad~anyany.png (= 2732x2732 = 1366x1366@2x)
Default@2x~ipad~comany.png (= 1278x2732 = 639x1366@2x)(Please save the images into res/screen/ios/)
In both of the two methods (1) and (2), after setting up the app correctly, the app will be shown in full screen.
Problem III has been fixed. But at the same time, Problem II (page contents hidden by the obstacles) occurred. So now let’s fix Problem II.
Tip:
Problem III need not always to be fixed because leaving Problem III as it is prevents Problem II. Since Problem II takes some costs, it would be a good choice when you would like to make an existing Cordova app compatible iPhone X with low cost.
Fix Problem II
As we showed before, Cordova app causes Problem II in both of portrait mode and landscape mode.
As same as PART I, basically we can fix Problem II by setting appropriate values to margin
and padding
in order to prevent boxes are hidden by the notch, the corners and the bar.
Onsen UI provides a patch CSS for portrait mode as well as landscape mode. Adding both of onsflag-iphonex-portrait
attribute and onsflag-iphonex-landscape
attribute to the html
element enables the patch CSS in both of portrait mode and landscape mode.
beforeMount() {
const html = document.documentElement;
if (this.$ons.platform.isIPhoneX()) {
html.setAttribute('onsflag-iphonex-portrait', '');
html.setAttribute('onsflag-iphonex-landscape', '');
}
}
The patch CSS works as follows:
Problem II in iPhone X WebView has been fixed.
Summary of PART II
In PART II, we explained that if we show Cordova apps in iPhone X WebView it causes two more problems below in addition to the problems in web apps:
- White blank areas appear in portrait mode as well as landscape mode
- Apps are not shown in full screen by default (Problem III)
Moreover, I explained how the iPhone X support for Cordova apps is more complicated than that for Web apps.
However, those problems can be easily fixed by setting viewport-fit=cover
and showing storyboard-based splash screen by using cordova-plugin-splashscreen
.
Even if you are developing Cordova apps, you can smoothly support iPhone X considering and solving Problem I, II and III one by one.
Now, we can create Cordova apps like iPhone X native apps by implementing page transition by using Onsen UI components such as Action Sheet(v-ons-action-sheet
) and Navigator(v-ons-navigator
):
The source code of the example Cordova app above is like:
)
The full source code of the app is available at this GitHub repo.
Conclusion
In this post, we talked about the iPhone X-specific problems of web apps (PART I) and those of Cordova apps (PART II). Especially, we explained the following three problems and how to fix them:
- Problem I — Blank areas on the edges of the screen, which can be fixed by
viewport-fit=cover
- Problem II — Page contents are hidden by the notch, the rounded corners and the bar, which can be fixed by setting appropriate
margin
andpadding
. - Problem III — Apps are not shown in full screen, which can be fixed by enabling storyboard-based splash screen.
The iPhone X support helper of Onsen UI (html[onsflag-iphonex-portrait]
and html[onsflag-iphonex-landscape]
) was also explained.
I hope this post helps you create amazing apps running on iPhone X. Happy coding!
What’s next
The Twitter account and the website of Onsen UI are here:
- ♨️ Onsen UI | Twitter
https://twitter.com/Onsen_UI - Onsen UI for Vue
https://onsen.io/vue/
References
[css-variables] User Agent properties and variables · Issue #1693 · w3c/csswg-drafts
https://github.com/w3c/csswg-drafts/issues/1693
(Aug 4, 2017)
[CB-13273] Webview is sized incorrectly on iPhone X (Simulator) — ASF JIRA
https://issues.apache.org/jira/browse/CB-13273
(Sep 13, 2017)
Removing the White Bars in Safari on iPhone X
http://stephenradford.me/removing-the-white-bars-in-safari-on-iphone-x/
(Sep 14, 2017)
ios — Cordova app not displaying correctly on iPhone X (Simulator) — Stack Overflow
https://stackoverflow.com/questions/46232812/cordova-app-not-displaying-correctly-on-iphone-x-simulator/46232813#46232813
(Sep 15, 2017)
34518947: UIWebView safe area inset constants are not updated on rotation · Issue #18415 · lionheart/openradar-mirror
https://github.com/lionheart/openradar-mirror/issues/18415
(Sep 20, 2017)
Designing Websites for iPhone X | WebKit
https://webkit.org/blog/7929/designing-websites-for-iphone-x/
(Sep 22, 2017)