*iroi*

mechairoi の Blog

iOS Safari でキーボード表示時にフッターを固定するもう1つの方法

zenn.dev

エディタを作っていると iOS Safari でキーボードを開いたときの挙動は本当に大変ですよね。 この記事では、キーボードを開いたままスクロールしてもフッターを固定できるようなワークアラウンドを最近見つけたので紹介します。 Safari 16 以降が必要。キーボード開閉時に追従が遅れる挙動は改善しません。

デモは https://mechairoi.github.io/ios-safari-virtual-keyboard/iPhoneiOS Simulator でお試しください。

GitHub - mechairoi/ios-safari-virtual-keyboard

Virtual Keyboard の挙動

Virtual Keyboard を開いたときには以下のようなことが起こっていると推測しています。

  • Visual_Viewport_API で取得できる Visual Viewport のサイズがキーボードを除いた領域のサイズまで小さくなります。
  • レイアウトの計算をするときにはキーボード表示前の Visual Viewport が使われます。vh 単位や fixed, sticky 要素の位置などは表示前と変わりません。
  • スクロールしたときに、キーボード表示前の Visual Viewport のうち画面に表示されている位置が変わることがあり、fixed 要素が動いて見えます。
    • Layout Viewport の上に大小二つの Visual Viewport が重なっていてそこから見ている感じです、ややこしいですね。
  • Layout Viewport の下?に謎の余白が追加されます。下にスクロールし続けると余白が表示されます。
  • キーボードと重なる位置にある入力要素をタップしてキーボードを開いたとき、キーボードに隠れないようにするためかスクロールが発生します。
    • また、キーボードを開いている状態で他の入力要素等にフォーカスが移動したときにもスクロールが発生します。

ワークアラウンド

まず画面全体にスクロール可能な要素を表示します。さらにSafari 16 から使えるようになった overscroll-behavior: contain をすることでスワイプやフリック操作による window のスクロールを防ぎます。window がスクロールしなければ固定した要素が動いて見えることはありません。

#root {
  overflow-y: scroll;
  overscroll-behavior: contain;
  width: 100svw;
  height: 100svh;
}

キーボード開閉時などどうしてもスクロールが発生してしまうタイミングでは JavaScript で計算して要素を移動したりするのはこれまでの方法と変わらないです。

ダイアログなど他の要素を画面に固定したいときは position:sticky を使うか、position:fixedposition:absolute で固定した要素の内側もスクロール可能にしてoverscroll-behavior: contain します。overscroll-behavior: contain しないと固定した要素上でスワイプしたときにwindow がスクロールしてしまいます。また選択範囲のすぐ隣にツールバー出すときはコンテンツに対して固定できれば良いので position:relativeposition:absolute が使えます。

他にも以下のような工夫をしてます。

  • コンテンツが少ないときも overscroll-behavior を効かせるために min-height を指定して微妙にスクロールできるようにします。
  • overscroll-behavior-y:contain があると Safari では pull to refresh できなくなってしまいます。キーボードを閉じているときは pull to refresh できるように overscroll-behavior-y:contain はキーボードが開いている間だけ有効にします。
  • maximum-scale=1ツールバーを非表示機能を使ったタイミングで微妙に拡大されてしまうのを防ぎます。指定していても最近のブラウザではピンチアウトで拡大できると思います…
  • キーボードを開いた時にカーソルがヘッダやフッタの下に隠れてしまうことがあるので調整が必要ですが、少し頑張ればなんとかなるでしょう。

この方法でも実現できるUIに制限があり現実的にはある程度妥協することになりそうです。Safariinteractive-widget=resizes-content に対応してくれるとかなり解決する気がするので、なんとかお願いします。

参考文献