【问题标题】:How to fix viewport in place when virtual keyboard opens in mobile Safari?在移动 Safari 中打开虚拟键盘时如何修复视口?
【发布时间】:2020-01-09 23:27:25
【问题描述】:

目标

在移动 Safari 上,当虚拟键盘打开时,屏幕应呈现如下图像:

地点:

  • 导航栏和输入是固定的
  • 短信列表可滚动

问题

在移动 Safari 上,当软键盘打开时,拖动输入或导航栏可以上下移动整个视口。

我有一段截屏视频演示了这个问题:https://youtu.be/GStBjRVpoGU

我在这个问题上花了几个月的时间,包括在 Stack Overflow 上搜索了许多类似的问题,但我一直无法找到可行的解决方案(或者,至少,我能够做出的解决方案工作)。

背景

此应用是一款混合移动应用:使用 React.js 作为 Web 应用构建,但使用 WebView 组件封装在 React Native 应用中。然而,即使在普通的移动 Safari 窗口中打开 Web 应用程序,也会存在同样的问题。

Mobile Safari 有一个相关的问题,即软键盘在打开时会将整个视口向上推,从而将视口的上半部分向上推并离开屏幕。 This excellent blog 提供了该问题的描述和解决方案。我实施了那个解决方案。它在软键盘打开时阻止视口向上移动,但在软键盘打开后视口仍然可以滑动。

另一个问题是移动版 Safari 在打开/关闭软键盘时不会更新 window.innerHeight。为了解决这个问题,我在 React Native 应用程序中使用了react-native-keyboard-spacer,有点像这样:

render() {
    return (
      <React.Fragment>
        <SafeAreaView >
          <WebView/>
        </SafeAreaView>
        <KeyboardSpacer />
      </React.Fragment>
    );
  }

这会在软键盘打开/关闭时改变 Webview 的高度,因此window.innerHeight 等也会改变。

众所周知,移动版Safari上的position: fixed;在打开软键盘时效果不佳,所以我改用position: absolute;,感谢其他very useful blog的建议。

我创建了一个code sandbox 来演示这个问题。在移动端Safari中打开可以看到虚拟键盘打开后的屏幕滑动。您也可以使用实际代码沙箱代码here,它代表了我最接近解决此问题的方法。

不过,关于代码沙箱需要注意的一点。没有 WebView 或 KeyboardSpacer:它只是一个网页。因此,我不得不硬编码一些高度。但如果你在移动版 Safari 中打开它,一旦打开软键盘,你会看到视口到处滑动。

以前有人见过这个特殊问题吗?你怎么修好它的?非常感谢。

【问题讨论】:

    标签: javascript css reactjs mobile-safari hybrid-mobile-app


    【解决方案1】:

    JMathew的帮助下,我找到了解决这个问题的办法。

    查看代码框示例here

    1. 将您的导航栏、消息列表和输入放入容器 div。
    2. 将引用添加到这些容器 div。例如:
      const messageListContainerRef = useRef<HTMLDivElement>(null);
    
      //...
    
      return (
        //...
        <div ref={messageListContainerRef}>
          <MessageList/>
        </div>
        //...
      )
    
    1. 当虚拟键盘打开时(当虚拟输入被聚焦时),为inputContainerRefnavbarContainerRef 添加一个touchmove 事件监听器。在触摸移动事件中将其用于preventDefault(),这将阻止用户上下滑动输入和导航栏。例如:
    if (inputContainerRef.current) {
      inputContainerRef.current.addEventListener('touchmove', (e) => {
        e.preventDefault();
      });
    }
    
    1. 您仍然希望能够滚动浏览消息列表,因此您不能只在上面preventDefault()。相反,使用this solution 来防止用户通过设置scrollTop 值滚动超过页面的底部(和顶部):
    if (messageListContainerRef.current) {
      messageListContainerRef.current.addEventListener('touchmove', (e: any) => {
        if (!e.currentTarget) {
          return;
        }
        if (e.currentTarget.scrollTop === 0) {
          e.currentTarget.scrollTop = 1;
        } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop +
                                                    e.currentTarget.offsetHeight) {
          e.currentTarget.scrollTop -= 1;
        }
      });
    }
    

    根据您的需要,您可以将这些 touchmove 事件侦听器设置在组件挂载上,而不是在输入焦点上。如果适合您,您甚至可以为整个 document.body 设置它们。

    【讨论】:

    • 感谢您提供此解决方案!上周我一直在与这种行为作斗争,终于找到了你的;)我有一个类似的网络聊天应用程序,有类似的问题。此外,当键盘不在时,我会显式将主体高度更新为视口大小(输入上的焦点和模糊事件 + body.height = window.innerHeight)
    【解决方案2】:

    Javascript 解决方案,终于得到了完美的解决方案。

    window.scrollBy(0, 100); // Scroll 100px downwards 
    window.scrollBy(100, 0); // Scroll 100px to the right
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-02
      • 2018-05-27
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多