Safari mobile (iOS) 遇到的天坑筆記 - 包含可以直接放棄的部分

現在放棄就可以下班了喔 ❤️

pull-to-refresh (iOS 15+)

沒有辦法透過 onscroll 或是任何方法知道 user 正在 pull-to-refresh,沒錯‌‌,沒有任何方法 (ref from: https://developer.apple.com/documentation/webkitjs/)

解決方法:當 scrollY 在 0 的時候, touchmove 是負數當作開始嘗試 pull-to-refresh (正在 overscroll) 的狀態

let startTouchY

function onTouchStart(event) {
  // We're going to try to guess whether the user is trying to pull-to-refresh only.
  if (event.touches.length > 1 || window.scrollY !== 0) {
    return
  }
  const [touch] = event.touches

  startTouchY = touch.clientY
}

function onTouchMove(event) {
  if (event.touches.length > 1 || !this.startTouchY) {
    return
  }
  const [touch] = event.touches

  const offsetY = this.startTouchY - touch.clientY
  // Pulling to refresh, offsetY smaller than 0 means scrolling down.
  if (offsetY < 0) {
    // ...Do anything you want when user pulling to refresh.
  }
}

function onTouchEnd(event) {
  startTouchY = null
}
Sample code for detecting pull-to-refresh.

Navigation bar/Address bar 縮放 (iOS 15+)

同樣的也沒有任何 event 可以知道 user 目前的操作使 navigation bar 變小還是變大,如果需要做出 full screen 效果,唯一的方式是透過 resize event 通知需要重新計算高度,而高度則需要借助 CSS variable 調整 (作法參考來自 https://zenn.dev/tak_dcxi/articles/2ac77656aa94c2cd40bf)

let vw
let vh

function setFillHeight() {
  if (vw === window.innerWidth && vh === window.innerHeight) {
    // 畫面尺寸沒有更動,所以不做任何事
    return
  }

  // 畫面尺寸有更動的時候重新計算高度
  vw = window.innerWidth
  vh = window.innerHeight

  const vhUnit = window.innerHeight * 0.01
  document.documentElement.style.setProperty('--vh', `${vhUnit}px`)
}

window.addEventListener('resize', setFillHeight)
// 記得手動初始化
setFillHeight()

// 沒有用到的時候也要記得回收 listener
window.removeEventListener('resize', setFillHeight)
https://zenn.dev/tak_dcxi/articles/2ac77656aa94c2cd40bf

另外也可以透過 safe-area-inset-bottom 來預留 address bar 的空間

Does Safari 15 finally fix viewport height?
Luke Channings’s blog
更完整的實作可以參考 Luke 的筆記

有些實作元素像是 drag&drop 的操作因為上下移動會造成 navigation bar (address bar) 變動,目前沒有相對應的處理方法 (像是 disable address bar 縮放的任何手段)

↓ 範例:navigator resize 造成的 dragging 中斷

https://user-images.githubusercontent.com/1218900/160944477-cae01126-b2bd-4555-a6f9-5d864637f1a9.mov

swipe-to-back

直接電死系列第三彈,透過手勢回到前/後一頁的功能沒有 event 可以知道開始手勢/觸發,所以當有 swipe 手勢偵測實作的時候,建議可以留左右 edge 的空間,避免手勢重疊 (大約 50px 的空間,如果有官方文件清楚定義 swipe-to-back 的空間 pixel 請告知小弟,非常感謝)

以上是避免手勢重複的部分,而如果想要 disable swipe-to-back 手勢的話,可以參考 https://pqina.nl/blog/blocking-navigation-gestures-on-ios-13-4/

基本上就是在想要 disable 手勢的元素上接收 touchstart event

<div style="height:100px">Our element that prevents navigation</div>

<script>
    const element = document.querySelector('div');

    element.addEventListener('touchstart', (e) => {
        // prevent swipe to navigate gesture
        e.preventDefault();
    });
</script>
https://pqina.nl/blog/blocking-navigation-gestures-on-ios-13-4/

Wrap it up

以上特別是 navigator (address bar) 花了小弟特別多時間,最令人難過的是沒有解決方法...雖然我也是比較喜歡 iPhone 帶給我的使用體驗,但開發起來真的只能芭比Q了

Credit: https://www.irasutoya.com