iPhone Duo対応で考える、変化する表示領域とUIの設計
アプリのアダプティビティから考える
iPhone Duoへの対応では、アプリのアダプティビティを考えることが重要です。
開閉によって表示領域が変わることは、物理的にウィンドウをリサイズすることとして捉えられます。レイアウトを考える上では、iPadのウィンドウサイズが変わる場合と共通しています。
iPad、Duo、iPhone、あるいは横画面を、それぞれ個別に考えないことが重要です。いずれもウィンドウの表示領域が変わるものとして、共通した考え方で設計します。コード上でも、レイアウトをidiomで分岐させないようにします。
コンテンツレイヤーとUIレイヤーを分ける
設計する際には、コンテンツレイヤーとUIレイヤーを分けて考えます。特にUIレイヤーでは、モデルを基準にデザインすることが重要です。

UIレイヤーにあるツールバーやナビゲーションバーは、アプリ側で見た目を自由にデザインする領域ではありません。ボタンの位置や形状を決めるのではなく、そこに配置する操作のシンボルやロール、用途をモデルとして定義します。
例えば、ツールバーに操作を追加するときは、「この位置に、この形のボタンを描く」と考えるのではなく、「何をする操作なのか」「どのシンボルで表すか」「どのようなロールを持つか」を考えます。その定義をもとに、具体的な配置や表示はシステムに委ねます。
このように設計すると、ウィンドウサイズによってバーの配置や見た目が変わっても、同じ操作の定義を使えます。アプリ側で表示ごとのデザインを作り分ける必要も少なくなります。
見た目を基準にUIレイヤーを作り込むと、システムが行うレイアウトの変化にも自前で対応する必要があります。コンテンツレイヤーの表現と、UIレイヤーの操作の定義を分けて考えることが重要です。
フォームファクタを活かす範囲

半開きの状態など、Duo固有のフォームファクタを活かすUIは、まだデザインパターンを模索している段階です。一般的なアプリへの導入を急ぐには時期尚早で、新しいUIを発明すること自体に強いこだわりがなければ、対応の優先度は低くなると考えています。
一方で、すでにこうした体験を探るアプリも登場しています。Tiny Computersは、半開きの画面をディスプレイとキーボードに見立て、ミニチュアのコンピュータを操作する体験を提供しています。MD Vinylでは、半開きの画面で、回転するレコードの上にアルバムのカバーを並べて探せます。
こうした先行例のキャッチアップは続けるべきです。まずは表示領域の変化に対応しながら、固有のフォームファクタがアプリの体験にどう貢献できるかを見極めていきます。
セーフエリアを正しく扱う
セーフエリアは、上下左右を考慮して扱う必要があります。その際、画面を2次元的に区切り、ナビゲーションバーやツールバーの空き領域にコンテンツを収めるだけでは、適切に扱えません。
スクロールビューはナビゲーションバーやツールバーの背後まで広がり、その上にUIレイヤーが重なっていると捉えます。スクロールビュー自体は画面の端まで広げ、コンテンツの見え方や操作に必要な余白を調整します。
この構造を前提にすると、スクロールに応じてナビゲーションバーやツールバーが縮小する際に、コンテンツの表示領域を広げられます。バーの領域とコンテンツの領域を固定的に分けてしまうと、こうした表示領域の調整の恩恵を受けられません。
具体的に行った対応
表示する情報量を変える
Duoを開いたときの表示には、大きく3つの方向があります。
- コンテンツを大きく表示する
- コンテンツをたくさん表示する
- コンテンツの文脈を伝える
今回のアプリでは、3つ目の方向で対応しました。タイムラインと選択中のポストを階層的に表示し、フォーカスしているポストがタイムラインから選ばれたものであることを伝えます。
実装にはUISplitViewControllerを使用しました。設定の組み合わせが多く、意図した表示になるように複数のパターンを試しています。

FABからツールバーへ移す
主要なボタンは、FABからツールバーへ移しました。ツールバーに配置することで、縦方向のバーに表示され、操作しやすくなります。

ただし、iPhoneではボタンが右上に配置されると指が届きにくくなるため、FABを残しています。
左右のセーフエリアに対応する
これまで左右のセーフエリアのinsetが0だった箇所は、余白が生まれたときにレイアウトが崩れていることがあります。特に、ステータスバー側に操作要素が入り込むと、操作できなくなるため注意が必要です。
中央寄せも、基準となる領域を確認する必要があります。ビューの端を基準に配置すると、セーフエリアを考慮したコンテンツ領域の中央にはならない場合があります。
レイアウトの更新に対応する
viewDidLoad時点のレイアウトに依存したコードは、Duoを開いたときに崩れやすくなります。
初回表示時の配置で完結させず、setNeedsLayout()によるレイアウト更新を前提とした実装へ書き直しました。表示領域が変わったときにも、その時点のサイズに応じて配置を更新できるようにします。
対応で難しかった点
タブバーの扱い
iPadと同じようにすべてのタブを表示する前提で実装すると、タブバーが窮屈になります。
compactTabIdentifiersを使うことで、compactな表示で見せるタブを減らせます。しかし、今回の構成では、非表示にしたタブを選択するUIがありませんでした。
そこでサイドバーを自前で実装し、iPadと同様の操作性で各タブへアクセスできるようにしました。サイドバーのカスタマイズで実現できる範囲は限られており、追加のUIは下部に配置しています。

ツールバーの扱い
基本的には、標準のツールバーAPIを使うほうが、縦方向のバーへの対応は行いやすくなります。
一方、今回の構成では、キーボードとツールバーが隣接してしまう挙動がありました。不具合かどうかは分かっていませんが、この箇所は自前で描画しています。

Split Viewの扱い
Split Viewでは、compactとregularで投稿ボタンをどこに配置するかが難しい点でした。
primaryとsecondaryの両方に配置すると、regularで両カラムが表示されたときにボタンが重複します。カラムの表示状態に応じた投稿ボタンの置き場所は、引き続き検討が必要です。