iOS 8 removed “minimal-ui” viewport property, are there other “soft fullscreen” solutions?

iOS 8 removed “minimal-ui” viewport property, are there other “soft fullscreen” solutions?

(This is a multi-part question, I will try my best to summarise the scenario.)
We are currently building a responsive web app (news reader) that allow users to swipe between tabbed content, as well as scroll vertically inside each tabbed content.
A common approach to the problem is to have a wrapper div that fills the browser viewport, set overflow to hidden or auto, then scroll horizontally and/or vertically inside it.
This approach is great but has one main drawback: since the height of the document is exactly the same as the browser viewport, the mobile browser will not hide the address bar/navigation menu.
There are numerous hacks and viewport properties that enable us to get more screen real estate, but none are quite as effective as minimal-ui (introduced in iOS 7.1).
News came yesterday that iOS 8 beta4 had removed minimal-ui from Mobile Safari (see Webkit section in iOS 8 Release Notes), which left us wondering:
Q1. Is it still possible to hide the address bar on Mobile Safari?
As far as we know, iOS 7 no longer responds to the window.scrollTo hack, this suggests we have to live with the smaller screen space, unless we adopt a vertical layout or use mobile-web-app-capable.
Q2. Is it still possible to have a similar soft fullscreen experience?
By soft fullscreen I really mean without using the mobile-web-app-capable meta tag.
Our web app is built to be accessible, any page can be bookmarked or shared using the native browser menu. By adding mobile-web-app-capable we prevent users from invoking such menu (when it’s saved to homescreen), which confuses and antagonises users.
minimal-ui used to be the middle-ground, hiding the menu by default but keeping it accessible with a tap — though Apple might have removed it due to other accessibility concerns (such as users not knowing where to tap to activate the menu).
Q3. Is a fullscreen experience worth the trouble?
It would seem that a fullscreen API is not coming to iOS anytime soon, but even if it is, I don’t see how the menu will be kept accessible (same goes for Chrome on Android).
In this case, maybe we should just leave mobile safari as it is, and account for viewport height (for iPhone 5+, it’s 460 = 568 – 108, where 108 includes the OS bar, address bar and navigation menu; for iPhone 4 or older, it’s 372).
Would love to hear some alternatives (besides building a native app).

Solutions/Answers:

Solution 1:

The minimal-ui viewport property is no longer supported in iOS 8. However, the minimal-ui itself is not gone. User can enter the minimal-ui with a “touch-drag down” gesture.

Related:  AngularJS - Image “onload” event

There are several pre-conditions and obstacles to manage the view state, e.g. for minimal-ui to work, there has to be enough content to enable user to scroll; for minimal-ui to persist, window scroll must be offset on page load and after orientation change. However, there is no way of calculating the dimensions of the minimal-ui using the screen variable, and thus no way of telling when user is in the minimal-ui in advance.

These observations is a result of research as part of developing Brim – view manager for iOS 8. The end implementation works in the following way:

When page is loaded, Brim will create a treadmill element.
Treadmill element is used to give user space to scroll. Presence of
the treadmill element ensures that user can enter the minimal-ui view
and that it continues to persist if user reloads the page or changes
device orientation. It is invisible to the user the entire time. This
element has ID brim-treadmill.

Upon loading the page or after changing the orientation, Brim is using
Scream to detect if page is in the
minimal-ui view (page that has been previously in minimal-ui and has
been reloaded will remain in the minimal-ui if content height is
greater than the viewport height).

When page is in the minimal-ui, Brim will disable scrolling of the
document (it does this in a safe
way
that does not affect
the contents of the main element). Disabling document scrolling
prevents accidentally leaving the minimal-ui when scrolling upwards.
As per the original iOS 7.1 spec, tapping the top bar brings back the
rest of the chrome.

The end result looks like this:

Brim in iOS simulator.

For the sake of documentation, and in case you prefer to write your own implementation, it is worth noting that you cannot use Scream to detect if device is in minimal-ui straight after the orientationchange event because window dimensions do not reflect the new orientation until the rotation animation has ended. You have to attach a listener to the orientationchangeend event.

Scream and orientationchangeend have been developed as part of this project.

Solution 2:

Since there is no programmatic way to mimic minimal-ui, we have come up with a different workaround, using calc() and known iOS address bar height to our advantage:

The following demo page (also available on gist, more technical details there) will prompt user to scroll, which then triggers a soft-fullscreen (hide address bar/menu), where header and content fills the new viewport.

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Scroll Test</title>

    <style>
        html, body {
            height: 100%;
        }

        html {
            background-color: red;
        }

        body {
            background-color: blue;
            margin: 0;
        }

        div.header {
            width: 100%;
            height: 40px;
            background-color: green;
            overflow: hidden;
        }

        div.content {
            height: 100%;
            height: calc(100% - 40px);
            width: 100%;
            background-color: purple;
            overflow: hidden;
        }

        div.cover {
            position: absolute;
            top: 0;
            left: 0;
            z-index: 100;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: rgba(0, 0, 0, 0.5);
            color: #fff;
            display: none;
        }

        @media screen and (width: 320px) {
            html {
                height: calc(100% + 72px);
            }

            div.cover {
                display: block;
            }
        }
    </style>
    <script>
        var timeout;

        window.addEventListener('scroll', function(ev) {

            if (timeout) {
                clearTimeout(timeout);
            }

            timeout = setTimeout(function() {

                if (window.scrollY > 0) {
                    var cover = document.querySelector('div.cover');
                    cover.style.display = 'none';
                }

            }, 200);

        });
    </script>
</head>
<body>

    <div class="header">
        <p>header</p>
    </div>
    <div class="content">
        <p>content</p>
    </div>
    <div class="cover">
        <p>scroll to soft fullscreen</p>
    </div>

</body>
</html>

Solution 3:

Just say goodbye to minimal-ui (for now)

It’s true, minimal-ui could be both useful and harmful, and I suppose the trade-off now has another balance, in favor of newer, bigger iPhones.

Related:  How to convert sequence of numbers in an array to range of numbers

I’ve been dealing with the issue while working with my js framework for HTML5 apps. After many attempted solutions, each with their drawbacks, I surrendered to considering that space lost on iPhones previous than 6.
Given the situation, I think that the only solid and predictable behavior is a pre-determined one.

In short, I ended up preventing any form of minimal-ui, so at least my screen height is always the same and you always know what actual space you have for your app.

With the help of time, enough users will have more room.


EDIT

How I do it

This is a little simplified, for demo purpose, but should work for you. Assuming you have a main container

html, body, #main {
  height: 100%;
  width: 100%;
  overflow: hidden;
}
.view {
  width: 100%;
  height: 100%;
  overflow: scroll;
}

Then:

  1. then with js, I set #main‘s height to the window’s available height. This also helps dealing with other scrolling bugs found in both iOS and Android. It also means that you need to deal on how to update it, just note that;

  2. I block over-scrolling when reaching the boundaries of the scroll. This one is a bit more deep in my code, but I think you can as well follow the principle of this answer for basic functionality. I think it could flickr a little, but will do the job.


See the demo (on a iPhone)

As a sidenote: this app too is bookmarkable, as it uses an internal routing to hashed addresses, but I also added a prompt iOS users to add to home. I feel this way helps loyalty and returning visitors (and so the lost space is back).

Related:  How do I create an abstract base class in JavaScript?

Solution 4:

The easiest way I found to fix this was to set the height of the body and html elements to 100.1% for any request where the user agent was an iphone. This only works in Landscape mode, but thats all I needed.

html.iphone, 
html.iphone body { height: 100.1%; }

Check it out at https://www.360jungle.com/virtual-tour/25

Solution 5:

The root problem here seems that iOS8 safari won’t hide the address bar when scrolling down if the content is equal or less than the viewport.

As you found out already, adding some padding at the bottom gets around this issue:

html {
    /* enough space to scroll up to get fullscreen on iOS8 */
    padding-bottom: 80px;
}
// sort of emulate safari's "bounce back to top" scroll
window.addEventListener('scroll', function(ev) {
    // avoids scrolling when the focused element is e.g. an input
    if (
        !document.activeElement
        || document.activeElement === document.body
    ) {
        document.body.scrollIntoViewIfNeeded(true);
    }
});

The above css should be conditionally applied, for example with UA sniffing adding a gt-ios8 class to <html>.

Solution 6:

I want to comment/partially answer/share my thoughts. I am using the overflow-y:scroll technique for a big upcoming project of mine. Using it has two MAJOR advantages.

a) You can use a drawer with action buttons from the bottom of the screen; if the document scrolls and the bottom bar disappears, tapping on a button located at the bottom of the screen will first make the bottom bar appear, and then be clickable. Also, the way this thing works, causes trouble with modals that have buttons at the far bottom.

b) When using an overflown element, the only things that are repainted in case of major css changes are the ones in the viewable screen. This gave me a huge performance boost when using javascript to alter css of multiple elements on the fly. For example, if you have a list of 20 elements you need repainted and only two of them are on-screen in the overflown element, only those are repainted while the rest are repainted when scrolling. Without it all 20 elements are repainted.

..of course it depends on the project and if you need any of the functionality I mentioned. Google uses overflown elements for gmail to use the functionality I described on a). Imo, it’s worth the while, even considering the small height in older iphones (372px as you said).