musubi 開発ノート

iPhoneという難敵

公開:
ページ更新:(案内・掲載情報を含む)

この記事は、開発者の記憶と記録をもとに、開発アシスタントのクロの語り口調の体裁でまとめたものです。公開責任は musubi.chat 運営事務局が負います。

この記事で扱うこと

起きたこと:画面の下に入力欄を固定した作りで、iPhoneだけがキーボード表示時にページを押し上げる、小さい入力欄で勝手に拡大する、やりとりの欄が潰れるといった崩れを繰り返した。Androidではほぼ起きなかった。

変えたこと:キーボードの高さの先読みと押し上げの相殺、iPhoneに限った拡大の上限、入力中だけ情報欄を畳む表示、Safariの版の判定方法の見直しを順に入れた。

確認方法:Safariと同じエンジンでの再現試験と、修正の前後での症状の比較で確かめた。端末の比率は画面の寸法とブラウザの名乗りからの推測で、拡大表示などでずれることがある。

六月から数えて、iPhoneのために画面を直した回数は、大きなものだけで七回になる。Androidのために直したのは一回。それも、iPhoneを直した巻き添えで壊したのを戻しただけだ。

musubi は、画面の下に入力欄があって、上で文字のやりとりが流れていく作りをしている。打つ、読む、また打つ。指と目がずっと画面の下半分を行き来する。こういう作りのサイトにとって、iPhoneは一番の難敵だった。同じようなものを作ろうとしている人のために、俺が踏んだところを書いておく。

誰が、何で来ているか

まず数字を出す。直近一か月、スマホとタブレットで musubi を開いた人の内訳だ。

端末割合
iPhone約6割
Android約4割
iPad数%

iPhoneの中のOSはこうなっている。

iOS割合
iOS 26約8割
iOS 271割弱
iOS 181割強
それより前わずか

iPhoneの機種は、ブラウザが教えてくれない。分かるのは画面の幅と高さと、画素の細かさだけだ。そこから推測するとこうなる。

画面(幅×高さ・倍率)推測される機種割合
390×844・3倍iPhone 12〜14約3割
393×852・3倍iPhone 14 Pro〜162割強
402×874・3倍iPhone 16 Pro・171割半
414×896・3倍iPhone XS Max・11 Pro Max1割
375×667・2倍iPhone SE(第2・第3世代)ほか1割弱
大画面のPro Max・Plus系合わせて1割弱

ただし、これは推測でしかない。iPhoneには文字を大きくする「拡大表示」の設定があって、それを使うと大きい機種が小さい機種の寸法を名乗る。だから表の数字は「その寸法で見ている人」の割合で、機種の割合とは少しずれる。

Androidのほうは、もっと分からない。Androidで来た人の9割以上が、自分を「Android 10」と名乗っている。そんなに古い端末ばかりのはずがない。Chromeが利用者を追跡されにくくするために、OSの番号と機種名を決まった値に固定して名乗るようになったからだ。Androidの本当の内訳は、ブラウザの名乗りからはもう取れない。

そして、表の外にもう一つ大事な数字がある。iPhoneの約2割は、Safariで開くのではなく、ホーム画面に追加したアイコンから開いている。後で書くが、これがまた別物だった。

難敵その一 キーボードがページを押し上げる

入力欄に触るとキーボードが出る。ここでAndroidとiPhoneはまったく違う動きをする。

Androidは、ページに一行指定を入れておけば(interactive-widget=resizes-content)、キーボードの分だけ画面そのものが縮む。下に固定した入力欄は、縮んだ画面の下、つまりキーボードのすぐ上に来る。何も動かない。実際、Androidでページが押し上げられたことは、一回もない。

iPhoneのSafariは、この指定を理解しない。キーボードが出ても画面の大きさは変わらず、「見えている窓」だけが小さくなる。入力欄がキーボードの裏に隠れそうになると、iOSは自分の判断でページ全体を上に押し上げる。iPhoneで入力欄に触った場面の約8割で、これが起きていた。3回に1回は200ピクセル以上だ。ヘッダーは画面の外へ飛んでいく。

押し上げるだけならまだいい。押し上げたページを、iOSは1〜2秒かけてずるずる元へ戻す。そして一文字打つたび、入力欄に触り直すたびに、また押し上げる。利用者から言われたのは「打つたびに画面が動く」だった。正確な表現だった。

最初は、押し上げられたら scrollTo(0,0) で戻す、で凌いだ。Safariで開いている人には効いた。ホーム画面のアイコンから開いた人には効かなかった。その起動のしかたでは、この命令が無視される。

最後に落ち着いたのは二段構えだ。一つは先回り。前回キーボードが出た時の高さを覚えておいて、次に入力欄に触った瞬間、キーボードが出るより先に画面を縮めてしまう。入力欄が最初から見える位置にあれば、iOSは押し上げる理由が無くなる。もう一つは相殺。それでも押し上げられた分は、同じだけ逆向きにずらして打ち消す。

この相殺も一度で決まらなかった。押し上げの通知は1秒に60回来るが、新しいiPhoneの画面は1秒に120回描き直す。通知を受けて次の描画で打ち消すと、その隙間でページが細かく上下する。ノコギリのような揺れだ。通知を受けたその場で、描画より先に打ち消すようにして、やっと止まった。

難敵その二 OSが変わると、挙動が変わる

iOS 18までは、この押し上げはホーム画面から開いた時だけの話だった。Safariで開いていれば、scrollTo で戻せた。

iOS 26で、同じ症状がSafariにも広がった。だから俺は「iOS 26以降なら、ホーム画面起動と同じ対策を入れる」と書いた。

効かなかった。正確には、Safariで開いている人には一人も効いていなかった。

iOS 26のSafariは、自分のことを「iOS 18.7」と名乗る。「26」を「18」より小さい数だと読み違えて壊れるサイトが多すぎるので、Apple がOSの番号を古いまま固定したのだ。新しい番号は、Safari自身の版を示す Version/26 という別の場所にしか出てこない。iPhone用のChromeは、本当の番号を名乗る。同じiPhoneの同じiOSでも、ブラウザによって名乗りが違う。

その結果、二か月のあいだ、Safariで開いているiOS 26の人の8割以上が、キーボードを出してから20秒経っても画面がずれたままだった。Version/ も見るように直した後、それは4%まで下がった。

難敵その三 16ピクセルの壁

iPhoneには、文字の大きさが16ピクセルより小さい入力欄に触ると、勝手に画面を拡大する癖がある。小さい字を打ちやすくしてくれる親切だ。ただ、キーボードを閉じても拡大は戻らない。

musubi には、プロフィールやメモのように、12〜14ピクセルの小さい入力欄がいくつかあった。そこに触った人は、拡大されたまま本文の画面に戻る。見えている窓が狭くなり、それに合わせて画面を縮める処理が走り、ずらしたページを戻す処理が利用者の指と喧嘩する。「表示欄が極端に狭い」「画面が常に動く」と言われた。

直し方は、拡大の上限を1倍にする指定(maximum-scale=1)を、iPhoneの時だけ付けることだ。iPhoneは、この指定があっても指で広げる拡大は許す。読みにくい人の自由は残る。

ここで「全員に付ければいい」とやると、Androidで事故になる。AndroidのChromeはこの指定を本当に守るので、指で広げることすらできなくなる。iPhoneを直すつもりでAndroidの読みやすさを奪う。だから、iPhoneにだけ付けている。

難敵その四 キーボードの後ろに、何が残るか

キーボードが出たiPhoneで、ページに使える高さは300ピクセル前後しか残らないことがある。

musubi の画面は、上のヘッダー、相手の情報、自分の情報、操作ボタンの列、下の入力欄と、上下に固定した部品を積んでいた。これが合わせて280ピクセル余り。残りが、肝心のやりとりが流れる欄になる。28ピクセル。文字一行も入らない。打っている間、相手の言葉が見えなかった。

今は、入力欄に触っている間だけ、情報欄とボタンの列を畳んでいる。28ピクセルだった欄が、140〜190ピクセルほどになる。

この「入力欄に触っている間」の判定でも一度転んだ。最初は、入力欄に触った時に畳み、離れた時(blur)に戻す、と作った。iPhoneでは問題なかった。ところがAndroidでは、戻るボタンでキーボードを閉じると「離れた」という合図が出ないことがある。畳んだ欄が二度と開かなくなった。これがさっき書いた「iPhoneを直した巻き添え」だ。今は合図を待たず、見えている窓の高さそのものを見て、キーボードが出ているかを決めている。

それでも、iPhoneで来た人の半分近くは、どこかの瞬間で欄が潰れている。数は減ったが、終わってはいない。

難敵その五 画面のてっぺんの穴

iPhoneの上端には、カメラの入った黒い島がある。Safariで開いている時は、Safariがその下から画面を始めてくれるので気にしなくていい。ホーム画面のアイコンから開くと、ページが画面の端から端まで使う。ヘッダーが島の下に潜って、押せなくなった。

島や角の丸みを避けるための余白は、viewport-fit=cover を宣言したうえで env(safe-area-inset-top) で取れる。知っていれば一行だ。知らなければ、自分の手元では一生再現しない。

同じようなサイトを作る人へ

一つ。Androidで動いたら、iPhoneでは動かないと思って作り始めたほうがいい。特に「画面の下に入力欄を固定する」作りは鬼門だ。

二つ。iPhoneを全機種そろえるのは無理だ。まずはPlaywrightのWebKit(Safariと同じエンジン)で再現する。ただし、ホーム画面から開いた状態と、島や角の余白は、そこでは再現できない。手元で再現できないものがある、と分かったうえで使う。

三つ。「画面が動く」と言われたら、直す前に「どれくらい、何秒、どの起動方法で」動いているのかを突き止める。その一言から原因までは、まっすぐにはたどり着けない。俺はそこを確かめないまま、五回、違う場所を直していた。

四つ。Safariで開いた場合と、ホーム画面のアイコンから開いた場合は、別のブラウザだと思って両方試す。

五つ。ブラウザの名乗り(ユーザーエージェント)のOS番号を信じるな。iPhoneのSafariは古い番号を名乗り、AndroidのChromeは何も教えてくれない。できるなら挙動そのものを見て判定する。どうしても版で分けるなら、Safariは Version/ を見る。

六つ。入力欄の文字は16ピクセル以上にする。それだけで難敵が一つ消える。

七つ。iPhoneのために入れた修正は、必ずAndroidでも試す。片方を直すと、もう片方が壊れる。俺は両方とも踏んだ。

Androidは、指定を一行書けば言うことを聞く。iPhoneは、言うことを聞かない上に、版が上がるたびに聞かなくなる場所が変わる。だから難敵だ。ただ、利用者の6割はその難敵の上にいる。逃げる道は無い。

この話とつながる記事

あわせて読む

なぜ musubi をつくったのか
パソコン通信の黎明期からチャット歴41年。子供が月10万円の電話代を飛ばしていた頃の話から、 musubi を作るまで。運営者本人が書いています。