5

As I understand it, the window.innerHeight should return the size of the viewport without the browser chrome (address bar, navigation, tabs, etc.). But this doesn't seem to be the case in the latest version of iOS13. Instead there are two problems:

  1. (Sometimes* too small in portrait) If you rotate from portrait mode to landscape mode with no tabs open and then back to portrait mode, the window.innerHeight value ends up being too small (by about the size of the bottom navigation bar) giving this horrible white bar at the bottom of the screen. See this discussion on macrumors for more details: https://forums.macrumors.com/threads/is-this-a-mobile-safari-bug-white-space-appears-at-bottom-after-rotating-iphone.2209551/
  2. (Sometimes* too big in landscape) If you have a bunch of tabs open, "Show tab bar" turned on and then rotate from portrait mode into landscape mode, then window.innerHeight is too big and the bottom of the screen gets cut off.

Even after turning on every conceivable viewport tag and all permutations thereof, it doesn't seem to work. I've also looked at several "tutorials" on how to handle this problem in iOS Safari, and to date every one that I've checked is broken.

I've also tried all variations of the window.innerHeight, with more or less the same result:

  • The new visual viewport API returns the same results, no different than window.innerHeight. Bottom is still truncated in landscape with tab bar and portrait mode still has the white bar at the bottom.
  • document.documentElement.clientHeight with various permutations of CSS (using 100vh, 100%, etc.) gives the same result. Ditto for getBoundingClientRect on various divs and combinations of div elements.
  • window.outerHeight and screen.height give the size of the full screen without browser chrome, which is generally too big and causes an overflow.
  • Also tried a bunch of other random things that I've forgotten by now (should have taken notes).

You can manually fudge it on a per-device basis if you can guess the size of the top and bottom browser chrome, but this is extremely fragile. I'm looking for a solution that doesn't involve building a giant look up table of every iOS device and software configuration.

I'm trying to make a fullscreen canvas element for a web game and this issue is blocking my ability to ship. As far as I know this issue is only present in iOS13. After looking around for weeks I still haven't found a good fix.

Mikola
  • 8,569
  • 1
  • 31
  • 41
  • We have a similar situation with a fullscreen game. There is no JS involved though, it's pure CSS but my guess is the root cause is the same. When the screen rotates from portrait to landscape, it's fine. But sometimes, when going back to portrait, it will cut part of the screen that matches the height of the ios status bar (with the time, etc). Now, when we rotate the phone 180º in portrait (so bottom is now at top), it will compute the height correctly again... – Kev Apr 30 '20 at 12:09

1 Answers1

1

I have had the same issue recently and I was able to solve it like this:

CSS (only relevant parts shown):

html {
    height: 100%;
    min-height: 100%;
    max-height: 100%;
    background: #99f; /* Safari for iOS and Opera for Android in fullscreen mode?!?! */
}

body {
    padding: 0;
    margin: 0;
    color: #000;
    width: 100%; /* I was desperate! This was a wild guess... And worked! */
    height: 100%;
    min-height: 100%;
    max-height: 100%;
    overflow: hidden;
    background: #99f;
}

TypeScript (only relevant parts shown):

// Assume everything here is in the global scope

function detectIOSOrSafari(): boolean {
    // https://stackoverflow.com/q/9038625/3569421
    if ((navigator.userAgent.indexOf("Chrome") <= 0 && navigator.userAgent.indexOf("Safari") >= 0) ||
        (navigator.userAgent.indexOf("Mac") >= 0 && ("ontouchend" in document)))
        return true;
    switch (navigator.platform) {
        case "iPad Simulator":
        case "iPhone Simulator":
        case "iPod Simulator":
        case "iPad":
        case "iPhone":
        case "iPod":
            return true;
    }
    return false;
}

const isIOSOrSafari = detectIOSOrSafari();

function adjustWindowSize(): void {
    let widthCss = window.innerWidth,
        heightCss = window.innerHeight;

    if (document.documentElement && ("clientWidth" in document.documentElement)) {
        widthCss = document.documentElement.clientWidth;
        heightCss = document.documentElement.clientHeight;
    }

    if (isIOSOrSafari) {
        let bodyRect: DOMRect = null;

        // Another act out of desperation...
        if (document.documentElement && ("getBoundingClientRect" in document.documentElement))
            bodyRect = document.documentElement.getBoundingClientRect();
        else if (("getBoundingClientRect" in document.body))
            bodyRect = document.body.getBoundingClientRect();

        if (bodyRect) {
            widthCss = bodyRect.right - bodyRect.left;
            heightCss = bodyRect.bottom - bodyRect.top;
        }
    }

    // Rest of the code, where I use widthCss and heightCss to compute my canvas' size
}

window.onresize = adjustWindowSize;

You can check out the entire source code in the project's repository: https://github.com/carlosrafaelgn/pixel

carlosrafaelgn
  • 601
  • 1
  • 13
  • 17