iOS 8移除了“minimal-ui”视口属性,是否有其他“软全屏”解决方案?

205

(这是一个多部分问题,我会尽力概括情境。)

我们正在构建一个响应式Web应用程序(新闻阅读器),允许用户在选项卡内容之间滑动,并在每个选项卡内容内部垂直滚动。

解决该问题的常见方法是使用包装器 div 来填充浏览器视口,将 overflow 设置为 hiddenauto,然后在其中水平和/或垂直滚动。

这种方法很不错,但有一个主要缺点:由于文档的高度与浏览器视口完全相同,手机浏览器将不会隐藏地址栏/导航菜单。

众多黑科技和视口属性可以使我们获得更多屏幕空间,但没有一个像 minimal-ui(iOS 7.1引入)那样有效。

昨天有消息称iOS 8 beta4已从Mobile Safari中移除了“minimal-ui”(请参见iOS 8 Release Notes中的Webkit部分),这让我们想知道:
问题1.在Mobile Safari上是否仍然可以隐藏地址栏?
据我们所知,iOS 7 不再支持“window.scrollTo”黑客技巧,这意味着我们必须接受较小的屏幕空间,除非我们采用垂直布局或使用“mobile-web-app-capable”。
问题2.是否仍然可以拥有类似的软全屏体验?
我真正想说的是没有使用“mobile-web-app-capable”元标记的软全屏。
我们的Web应用程序建立在可访问性基础上,任何页面都可以使用本机浏览器菜单进行书签或共享。通过添加mobile-web-app-capable,我们防止用户调用此类菜单(当它保存到主屏幕时),这会使用户感到困惑和敌对。 minimal-ui曾经是一个折中方案, 默认情况下隐藏菜单,但通过轻触使其可访问--尽管苹果可能由于其他可访问性问题(例如用户不知道在哪里轻触以激活菜单)而将其删除。 Q3. 全屏体验是否值得麻烦? 似乎iOS不会很快推出全屏API,但即使有,我也看不出菜单如何保持可访问性(安卓上的Chrome也是如此)。
在这种情况下,也许我们应该保持移动版Safari的原样,并考虑视口高度(对于iPhone 5+,它是460=568-108,其中108包括操作系统栏、地址栏和导航菜单;对于iPhone 4或更早版本,它是372)。
很想听听其他选择(除了构建本地应用程序)。

请参阅 https://dev59.com/oWMk5IYBdhLWcg3w-Spm ,了解为什么 minimal-ui 对某些应用程序至关重要。 - bitinn
1
我在iOS 7上遇到了同样的问题,因为我们正在构建一个具有滑动/滚动事件的Web应用程序,但是在iOS8 Beta 4上测试了onScroll事件,它们可以正常工作。http://ios8-scroll-events.heroku.com不确定这是否有所帮助,但是..至少你有这个。 - Devin M
遇到了同样的问题。目前只有JavaScript的“修复”方法,因为下面的calc()函数是唯一的答案。如果您知道更好的解决方案,请继续更新此线程。最好的问候。 - Coderbit
9个回答

95

在iOS 8中,minimal-ui viewport属性不再受支持。但是,minimal-ui本身并没有消失。用户可以通过“触摸向下拖动”手势进入minimal-ui。

管理视图状态有几个先决条件和障碍,例如,为了使minimal-ui起作用,必须有足够的内容以使用户可以滚动;为了使minimal-ui持续存在,窗口滚动必须在页面加载和方向更改后被偏移。然而,使用screen变量无法计算minimal-ui的尺寸, 因此无法提前告知用户何时处于minimal-ui状态。

这些观察结果是开发iOS 8的Brim-视图管理器的一部分研究的结果。最终实现方式如下:

当页面加载时,Brim将创建一个跑步机元素。跑步机元素用于给用户滚动的空间。跑步机元素的存在确保用户可以进入最小化UI视图,并且如果用户重新加载页面或更改设备方向,则该视图会继续存在。它在整个时间内对用户不可见。这个元素的ID是brim-treadmill
在加载页面或更改方向后,Brim使用Scream来检测页面是否处于最小化UI视图中(如果内容高度大于视口高度,则之前已经处于最小化UI视图中并且已经重新加载的页面将继续保持在最小化UI视图中)。
当页面处于最小化UI视图中时,Brim将禁用文档的滚动(它以一种安全的方式进行,不影响主元素的内容)。禁用文档滚动可以防止在向上滚动时意外离开最小化UI。根据原始的iOS 7.1规范,点击顶部栏会恢复其余的Chrome。
最终结果如下:

Brim in iOS simulator.

为了文档化,并且如果您想编写自己的实现,值得注意的是,在orientationchange事件后立即使用Scream以检测设备是否处于最小UI中是不可能的,因为window尺寸在旋转动画结束之前不会反映新的方向。你必须将监听器附加到orientationchangeend事件。 Screamorientationchangeend是该项目的一部分开发出来的。

3
这个回答比我的原始回答更详细,标记为新回答,直到找到更好的解决方案为止 :) - bitinn
4
看起来不错!我能在初始滚动时强制使用minimal-ui吗? - INT
59
这真的是一个永无止境的故事。我是一名HTML和minimal-ui游戏开发者,在iOS 7.1上运行得非常顺利——这是唯一一种可以全屏运行应用程序并同时能够在屏幕底部触摸的方法。页面滑动的解决方案很不错,但还不够好:(苹果公司,请为游戏提供适当的全屏API实现。 - Petr Urban
6
@Petr:我完全同意。当7.1宣布这个修复后,我们迅速推出了一个新的结账购买流程,将主要的CTA固定在屏幕底部... 这个方案非常有效,并且转化率很高!它感觉非常“本地”和无缝,但我认为这正是问题所在。如果你仔细想想,对于网页应用程序来说,让它们感觉像本地应用程序并不符合苹果的最佳利益。实际上,这直接与他们的App Store垄断权利相冲突。在我看来,这是唯一一个合理的原因,解决了一个有很大意义的功能,然后故意删除了它。#我的看法 :) - Jose Browne
2
@PetrUrban 我相信苹果公司更愿意您将游戏发布为PhoneGap应用程序,而不是允许您通过网络提供服务。他们最近决定允许Safari广告拦截器巩固了这个想法。 - Patrick Gunderson
显示剩余6条评论

21

由于没有编程方式可以模拟minimal-ui,我们想出了一种不同的解决方法,利用calc()和已知的iOS地址栏高度来实现:

下面的演示页面(也可在gist上获取,其中有更多技术细节)将提示用户滚动,这将触发软全屏(隐藏地址栏/菜单),其中标题和内容填充新的视口。

<!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>

12

暂时告别 minimal-ui

没错,minimal-ui 可以既有用处又有害处,我想现在的权衡已经偏向于较新、更大的 iPhone 了。

我在为 HTML5 应用程序开发框架工作时遇到了这个问题。尝试了许多解决方案,每个都有其缺点,最终我认为在早期的 iPhone 上放弃这些空间。

鉴于情况,我认为唯一坚实可靠的行为是预先确定好。

简而言之,我最终禁止任何形式的 minimal-ui,因此至少我的屏幕高度始终相同,您总是知道您的应用程序实际上有多少空间。

随着时间的推移,足够的用户将拥有更多的空间。


编辑

我是如何做到的

这只是为演示目的而简化了一下,但应该适用于您。假设您有一个主容器。

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

然后:

  1. 使用JavaScript将#main的高度设置为可用窗口高度。这也有助于处理iOS和Android中发现的其他滚动错误。这意味着您需要处理如何更新它,只需注意以下内容;

  2. 当滚动到边界时,我会阻止超出滚动范围。这个在我的代码中比较深入,但是我认为您也可以遵循此答案的基本功能原则。我认为它可能会稍微闪烁一下,但是能够完成任务。


查看演示(在iPhone上)

作为附注:由于应用程序使用内部路由到哈希地址,因此也可以加为书签,但我还添加了提示iOS用户将其添加到主屏幕上。我觉得这样有助于忠诚度和回头客(因此丢失的空间恢复了)。


禁用最小UI对我来说似乎非常合理。请提供如何这样做的简短描述! - István Pálinkás
你说得对,我加了一点点如何操作的说明。还有很多其他方法也可以。 - Francesco Frapporti
2
你的演示在iOS8上无法运行,根据我的iPhone 5。 - dmr07
谢谢您让我知道,这可能是因为某些更新,因为它以前是可以工作的。您在使用Safari浏览器吗?您所说的“不工作”具体指什么? - Francesco Frapporti
我想在手机上全屏显示。我假设 #main 包含您想要显示的所有元素。请问 .view 用于什么? - Janaka

7

我发现最简单的解决方法是在用户代理为 iPhone 的任何请求中将 body 和 html 元素的高度设置为 100.1%。这仅适用于横向模式,但对我来说就足够了。

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

你可以在 https://www.360jungle.com/virtual-tour/25 查看。


感谢@Stephen。height: 100.1%对我很有帮助。 然而,当我在iPhone(iOS 11.1.1)Safari上打开https://www.360jungle.com/virtual-tour/25并点击底部的按钮时,地址和工具栏出现了。这是因为按钮离显示器末端太近了。我猜在移动模式下将它们移到其他地方会更好。 - Téwa

2
这里的根本问题似乎是,如果内容等于或小于视口,则iOS8 safari在向下滚动时不会隐藏地址栏。
正如您已经发现的那样,添加一些底部填充可以解决这个问题:
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);
    }
});

上述CSS应该有条件地应用,例如通过UA嗅探将gt-ios8类添加到<html>中。

1
这段 JavaScript 代码具体做了什么? - pronebird
如果您正在参考 scrollIntoViewIfNeeded,那么它是 scrollIntoView(https://developer.mozilla.org/en-US/docs/Web/API/Element.scrollIntoView)的非标准派生版本。如其名称所示,该方法将元素滚动到可视区域内。 true 参数表示将视图与元素顶部对齐。这实际上应该防止您滚动。然而,具体实现是有缺陷的。 - Gajus

1

使用类似我下面示例的方法是可行的,这个示例是在(https://gist.github.com/bitinn/1700068a276fb29740a7)的帮助下编写的,但在iOS 11上没有完全生效:

以下是修改后在iOS 11.03上运行的代码,请在评论中说明是否有效。

关键是给BODY添加一些大小,以便浏览器可以滚动,例如:height: calc(100% + 40px);

以下是完整示例和在浏览器中查看链接(请测试!)

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CodeHots iOS WebApp Minimal UI via Scroll Test</title>

    <style>
        html, body {
            height: 100%;
        }
        html {
            background-color: red;
        }
        body {
            background-color: blue;
            /* important to allow page to scroll */
            height: calc(100% + 40px);
            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;

        function interceptTouchMove(){
            // and disable the touchmove features 
            window.addEventListener("touchmove", (event)=>{
                if (!event.target.classList.contains('scrollable')) {
                    // no more scrolling
                    event.preventDefault();
                }
            }, false); 
        }

        function scrollDetect(event){
            // wait for the result to settle
            if( timeout ) clearTimeout(timeout);

            timeout = setTimeout(function() {
                console.log( 'scrolled up detected..' );
                if (window.scrollY > 35) {
                    console.log( ' .. moved up enough to go into minimal UI mode. cover off and locking touchmove!');
                    // hide the fixed scroll-cover
                    var cover = document.querySelector('div.cover');
                    cover.style.display = 'none';

                    // push back down to designated start-point. (as it sometimes overscrolls (this is jQuery implementation I used))
                    window.scrollY = 40;

                    // and disable the touchmove features 
                    interceptTouchMove();

                    // turn off scroll checker
                    window.removeEventListener('scroll', scrollDetect );                
                }
            }, 200);            
        }

        // listen to scroll to know when in minimal-ui mode.
        window.addEventListener('scroll', scrollDetect, false );
    </script>
</head>
<body>

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

</body>

完整示例链接在这里: https://repos.codehot.tech/misc/ios-webapp-example2.html


1

我想进行评论/部分回答/分享我的想法。我正在为一个即将启动的大型项目使用overflow-y:scroll技术。使用它有两个主要优点:

a) 您可以在屏幕底部使用带有操作按钮的抽屉;如果文档滚动并且底部栏消失,则在屏幕底部位置上点击一个按钮,将首先使底部栏出现,然后才能被点击。此外,这种情况下的工作方式会与模态框有问题,因为模态框底部有按钮。

b) 当使用溢出元素时,在发生重大css更改的情况下,只有屏幕可见的内容会被重新绘制。当使用JavaScript实时修改多个元素的CSS时,这给了我巨大的性能提升。例如,如果您有一个包含20个元素的列表需要重新绘制,并且只有其中两个元素在溢出元素中可见,则只有这些元素在滚动时才会被重新绘制。否则,所有20个元素都需要重新绘制。

当然,这取决于项目和是否需要我提到的任何功能。Google在Gmail中使用溢出元素来使用我在a)中描述的功能。在我看来,即使考虑到更旧的iPhone的小高度(如您所说的372px),这也是值得的。


0

-3

我没有做过iOS的网页设计,但从我在WWDC会议和文档中看到的内容来看,Mobile Safari中的搜索栏和整个操作系统中的导航栏现在会自动调整大小以显示更多的内容。

您可以在iPhone上的Safari中进行测试,并注意到当您向下滚动以查看页面上的更多内容时,导航/搜索栏会自动隐藏。

也许保留地址栏/导航栏不变,而不是创建全屏体验,是最好的选择。我不认为苹果会很快这样做。最多他们不会自动控制地址栏何时显示/隐藏。

当然,在iPhone 4或4S上,您会失去屏幕空间,但在Beta 4中似乎没有其他选择。


1
我知道iOS7+的这个功能,但请看我上面的解释:由于文档的高度与浏览器视口完全相同,移动浏览器不会隐藏地址栏/导航菜单,因为在文档级别上没有滚动发生。 - bitinn
1
这可能是一个限制,因为Beta 4已经删除了该功能。很可能苹果正在自动控制地址栏并阻止开发人员访问它。 - iFeli
8
我还没有做过iOS的网页设计。但如果你从事网页设计,你要为每个平台进行设计,因为网络存在于每个平台上。 - ProblemsOfSumit
4
我知道在网页上工作是通用的,但每个浏览器及其基础框架都有特定的CSS属性。因此,Chrome可能有一些在Safari和FireFox中不可用的属性,反之亦然。 - iFeli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接