Supporting iPhone X for mobile web & Cordova app using Onsen UI

Final Output

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.

  1. It has a “notch” on the top.
  2. Rounded corners.
  3. A black bar is always present on the bottom.
(Source: https://developer.apple.com/ios/human-interface-guidelines/overview/iphone-x/ )

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 or body { 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 and padding to outermost boxes. (If you are using Onsen UI, it can be done by attaching onsflag-iphonex-portrait and onsflag-iphonex-landscape attributes to html 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.

Tab bar pattern (Source: https://developer.apple.com/ios/human-interface-guidelines/bars/tab-bars/ )

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 import
import 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.

App.vue
NotesPage.vue

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:

Tab bar pattern in iPhone X Safari (portrait mode)

It seems fine in the case of portrait mode. Then, let’s see what happens if we rotate the screen.

Tab bar pattern in iPhone X Safari (landscape mode)

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 */
}
In the case that background-color is set to black

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.

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.

The notch, the rounded corners and the bottom bar are hiding some part of the 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.

We must set margin and padding so that texts and figures don’t go into the red area

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 paddingto 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/)
Landscape safe area (Source: https://developer.apple.com/ios/update-apps-for-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 attention
The official WebKit blog explains how to support safe area with a new CSS function constant() and four constants safe-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 constants safe-area-inset-* have two nice characteristics: “the value is set only if viewport-fit is set to cover “ and “the value is automatically switched between the portrait value and the landscape value if the screen rotates.”
But constant() function and the four constants safe-area-inset-* have the following problems:
(1) constant() function has been removed from the CSS standard and renamed to env() 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() and env() don’t work in browsers except for Safari as of Nov, 2017, and it causes errors if we write codes such as padding-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.

After adding onsflag-iphonex-landscape attribute to the html element

Considering 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 import
import 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.

App.vue
NotesPage.vue

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"
Tab bar pattern in iPhone X WebView (portrait mode)

Oh, in the case of Cordova apps, it seems that the blank areas appear even in portrait mode.

Then, what happens in landscape mode?

Tab bar pattern in iPhone X WebView (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">
After setting the value of viewport-fit to 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 the viewport-fit points to contain , we have to change it to cover . But in WKWebView, auto points to cover by default (confirmed by the author). It means that Problem I is already fixed in WKWebView and we no longer have to set viewport-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.

Cordova app after Problem I is solved (portrait mode)
Cordova app after Problem I is solved (landscape mode)

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 (= 750
x1334 = 375x667@2x)
Default@2x~iphone~comcom.png (=
750x750 = 375x375@2x)
Default@3x~iphone~anyany.png (= 2436x2436 = 812x812@3x)
Default@3x~iphone~anycom.png (= 2436
x1242 = 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.

Cordova app shown in full screen (portrait mode)
Cordova app shown in full screen (landscape mode)

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:

After enabling the patch CSS (portrait mode)
After enabling the patch CSS (landscape mode)

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 ):

How v-ons-action-sheet and v-ons-navigator work

The source code of the example Cordova app above is like:

Example of iOS-like page transition (Source: https://github.com/asial-matagawa/vue-onsenui-cordova-iphonex )

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 and padding.
  • 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:

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)