如何在屏幕方向更改后在 iOS Safari 上保持应用程序全屏显示

How to keep an app fullscreen on iOS Safari after screen orientation changes

我构建了一个 SPA,该 SPA 应始终占用目标浏览器组的 100% 屏幕高度 - iOS(移动)Safari。 height:100vh 在 iOS Safari 上无法正常工作 -

https://css-tricks.com/css-fix-for-100vh-in-mobile-webkit/

关于使用某些资源的建议解决方案 -webkit-fill-available 没有帮助。

因此,我决定用JS控制app容器的高度:

const [height, setHeight] = useState(window.innerHeight);

  const handleWindowSizeChange = () => {
    setHeight(window.innerHeight);
  };
  useEffect(() => {
    window.addEventListener("resize", handleWindowSizeChange);
    return () => window.removeEventListener("resize", handleWindowSizeChange);
  }, []);
  return (
    <div className="App" style={{ height: height }}>
      <header className="top"></header>
      <div className="main"></div>
      <footer className="bottom"><button>Click</button></footer>
    </div>

我的解决方案一直有效,直到我们将屏幕从纵向旋转到横向,然后再旋转回纵向。在这些旋转之后,我们在屏幕视图的底部有一个间隙,就在应用程序页脚的正下方。如果我们打开,可以注意到相同的行为 一个演示 - https://react-div-100vh.vercel.app/ 一个应该解决问题的包,并进行相同的屏幕旋转。

浏览器:Safari 14.6 iOS/iPhone 7

Repository

Live app

CodeSandbox

视口高度在 iOS Safari 上很棘手。

根据用例,-webkit-fill-available 可以工作 (see article)。

我大部分时间都必须使用这个 JavaScript/CSS var 修复:

.my-element {
  height: 25vh;
  height: calc(25 * var(--vh, 1vh));
}

<script type="application/javascript">
  function handleResize () { window.document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`); }
  window.onresize = handleResize
  handleResize()
</script>

灵感:https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

import React from 'react'

const getHeight = () => {
    const html = document?.documentElement
    return Math.max(window?.innerHeight, html?.clientHeight, html?.offsetHeight)
}

const getWidth = () => {
    const html = document?.documentElement
    return Math.max(window?.innerWidth, html?.clientWidth, html?.offsetWidth)
}

const isIPad = (platform, maxTouchPoints) =>
    maxTouchPoints && maxTouchPoints > 2 && /MacIntel/.test(platform)

const isIPhoneOrIPad = (platform, touchpoints) =>
    platform === 'iPhone' ||
    /iPad/.test(platform) ||
    isIPad(platform, touchpoints)

const isIDevice = isIPhoneOrIPad(
    window?.navigator?.platform,
    window?.navigator?.maxTouchPoints,
)

const isIOSPWA = window.navigator['standalone'] === true

const MyApp = (props) => {
    const rafTimer = React.useRef(null)
    const timer = React.useRef(null)

    React.useEffect(() => {
        if (!isIDevice) return

        const iosHack = () => {
            const height = getHeight()
            const width = getWidth()
            document.body.style.minHeight = `${height}px`
            document.body.style.maxHeight = `${height}px`
            if (!isIOSPWA && height > width) {
                clearTimeout(timer.current)
                timer.current = setTimeout(() => {
                    const height = getHeight()
                    document.body.style.minHeight = `${height}px`
                    document.body.style.maxHeight = `${height}px`
                    clearTimeout(timer.current)
                    timer.current = setTimeout(() => {
                        const height = getHeight()
                        document.body.style.minHeight = `${height}px`
                        document.body.style.maxHeight = `${height}px`
                    }, 300)
                }, 300)
            }
        }

        cancelAnimationFrame(rafTimer.current)
        rafTimer.current = requestAnimationFrame(iosHack)

        return () => {
            cancelAnimationFrame(rafTimer.current)
            clearTimeout(timer.current)
        }
    })

    return <div>{props.children}</div>
}

export default MyApp

解决方案只是对 HTMLBody#root 使用 % 而不是 vh(以防万一 react-apps使用 create-react-app) 元素创建:

html,
body,
#root {
  height: 100%;
}

在此之后,根元素中的内容会占据整个屏幕的高度,并会在浏览器切换时进行调整 appear/disappear。 (在 iOS Safari 15.3.1 和 Chrome iOS v99.0.4844.59 上测试)

来源:The article about CSS Reset from and the course of the same author