WebアプリケーションのiOS12.2対応
3月末にiOS12.2がリリースされました。弊社ではブラウザ上で動作するデジタルカタログ等のWebアプリケーションの開発をしていますが、iOSやAndroidのバージョンが変わるとブラウザの動作も結構変わるので、アプリケーションの修正作業が必要になることが多くあります。
今回は、iOS12.1から12.2へのマイナーバージョンアップでしたが、微妙な違いがあり、何箇所かアプリケーションを修正しなければいけない箇所がありました。そのうちの一つ、端末(iPhone7/8)の向きを変えた時のSafariの挙動の違いについて説明したいと思います。
iOS12.2で端末の向きを横から縦にした時の違い
通常、iPhone7/8のSafariではスクロール位置がページ最上部の場合は図1のようにアドレスバーと画面下部にステータスバー(?)が表示されています。
iOS12.1では端末の向きを横から縦にした時に図1のようなアドレスバーとステータスバーが表示された状態に戻りますが、iOS12.2では図2のようにアドレスバーと画面下部のステータスバーが非表示状態になります。


これ自体は問題ないのですが、iOS12.2において横→縦のorientationchangeイベント後にwindow.innerWidth,window.innerHeightからウィンドウサイズを取得すると、正しい高さを取得できません。具体的には図3のようにアドレスバー、ステータスバーが表示されている状態の高さが返されます。
Webアプリケーションでは、スクリーンサイズを取得して適宜ユーザインタフェース等の表示位置を調整することが多いのですが、誤った高さを元にレイアウトするため、 余白が出来てしまいます(図3右)。
この状態から、画面上部を触れてアドレスバーが表示されると図3左の状態に戻りはするのですが、、、

上記の現象を確認するためのHTMLのサンプルが以下のようになります。簡単に動作を説明すると、resize,orientationchangeイベントが発生すると、window.innerWidth,window.innerHeightからウィンドウサイズを取得し、背景のグレーのエリアをウィンドウサイズいっぱいに覆うようにリサイズしています。
現象が発生するHTMLの例
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta id="viewport" name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0"> <title></title> <script> window.onload = function () { function dumpScreenSize(tag) { console.log(tag + ':' + window.innerWidth + 'x' + window.innerHeight); } function resizeBackground() { var element = document.getElementById('bglayer'); element.style.width = window.innerWidth + 'px'; element.style.height = window.innerHeight + 'px'; } window.addEventListener('resize', function () { dumpScreenSize('onresize'); resizeBackground(); }); window.addEventListener('orientationchange', function () { dumpScreenSize('onorientationchange'); resizeBackground(); }); dumpScreenSize(''); resizeBackground(); } </script> </head> <body style="padding:0px;margin:0px;"> <div id="bglayer" style="position:absolute;background-color:#cccccc;"> <p>test page</p> </div> </body> </html>
上のHTMLではイベント毎にログを出力するようにしているので、ログを確認してみます。
端末の向きを横→縦に変更した時に、iOS12.1で出力されるログは以下のようになります。resize,orientationchange両イベントとも375x553 pxのサイズが取得できています。このサイズはウィンドウサイズとして正しい値なので、グレーのエリアを画面いっぱいに正しくリサイズできます。
[Log] onresize:375x553 [Log] onorientationchange:375x553
一方、iOS12.2では以下のようになります。イベントの発生順が変わっていたりと細かい違いはありますが、最後に発生したresizeイベントでiOS12.1の時と同じ375x553 pxのサイズが返されています。実際の画面の表示は図3右のようになっているため、375x553 pxに合わせてサイズ調整すると、画面下に余白ができることになります。
[Log] onorientationchange:667x375 [Log] onresize:375x553
原因は何か
window.innerHeightが実際のウィンドウサイズと異る原因はなんでしょうか?
調べていくと、resizeイベント直後のwindow.innerHeightは誤った値になっていますが、しばらくしてからwindow.innerHeightにアクセスすると正常な高さを取得できるようでした。Safariの中の実装のことまではわかりませんが、iOS12.2になってからwindow.innerHeightの更新契機が変わってしまっているようです。
対策
対策として、向きを横→縦に変更した時に、iOS12.1と同じようにアドレスバーを表示した状態にできないかと色々試してみましたが難しいようでした。
そこで、イベント発生後しばらくしてからなら正しい高さを取得できるので、以下のようにorientationchange後、レイアウト処理を行うまでにディレイを入れるようにしました。
window.addEventListener('orientationchange', function () { setTimeout(function () { dumpScreenSize('onorientationchange'); resizeBackground(); }, 500); });
ディレイを入れた場合のログは以下のようになります。
[Log] onresize:375x553 [Log] onorientationchange:375x584 ← ディレイ後は正しい高さを取れている [Log] onresize:375x553 ← リサイズしたせいでアドレスバーが表示され、resizeイベントが再度発生している
onorientationchange:375x584のログから、ディレイ後は正しい高さを取れていることがわかります。このサイズを元にグレー領域をレイアウトすれば、図3右の画面をグレー領域で覆い尽すことができます。
想定外だったのは、375x584pxでグレー領域をレイアウトすると、それを契機にアドレスバーが表示され、結局図3左の状態になったことです。この時、resizeイベントは発生し、ウィンドウサイズも正しく取得できるので、結果として表示は正しく行えることになります。
この対策にあたって、どれだけディレイをいれればいいのかは試してみるしかないので、あまりいい対策ではないのですが、Webアプリケーションではこのようなタイミングに依存する問題は割と発生します。
iOSではバージョンによってSafariの動作がちょこちょこ変わるので、対応が結構大変なブラウザだったりします。以前はInternet Explorerが筆頭でしたが。
ころころと動作が変るブラウザに対応していかないといけないのが、Webアプリケーション開発の難しいところです。
=============弊社ではデジタルカタログ等のHTML5を使ったWebアプリケーションの開発を行っています。
=============投稿日:2019/04/23 23:54