デザイナーのためのiOS実装入門
先日、友人のデザイナーさん達とランチした際に、1人のデザイナーさん(Webデザインがメイン、軽いコーディングまでなら経験あり)が
・iOSアプリデザインやUXデザインにも興味ある
・より良いデザインやユーザー体験を実現できるようになるために
アプリ開発もしてみたい(特にデザイン上の制約を知るために)
と言っており、同じニーズを持つデザイナーさんやディレクターの方も多そうだったので、この記事を書いてみました。
対象読者
・iOSアプリのUI面で、できる/できるけどしんどい/できないことを知りたい
デザイナー、ディレクター
解説すること
・アプリで画面が表示されるまでの仕組み
・アプリの画面を実装する上でよく使うコンポーネント(パーツ)
・気をつけるべきポイント
実際の実装については、Qiitaなどでいくらでも解説されていると思いますので、イメージを掴んでもらうための概念的な説明をメインにしていきます。
WebとiOSアプリの画面遷移の違い
iOSアプリのUIに関する説明に入る前に、WebとiOSアプリの画面遷移の違いについてざっくりと説明したいと思います。
大きな違いは以下の2点です。
・いつ、画面の実体が生成されるか
・誰が、画面遷移を担当するのか
まず、Webにおける画面遷移ですが、表示されるページの実体は全てサーバ上に存在しており、画面遷移はブラウザが表示されるページを切り替えていく仕組みになっています(静的サイトの場合)。
それに対し、iOSアプリでは表示される画面の実体は基本的には画面遷移前には存在しておらず、画面のひな形(=クラス)のみが存在しています。
そして、画面遷移のタイミングでひな形から実体化(=インスタンスの生成)が行われ、元の画面や画面のいれもの(以後、コンテナと呼ぶ)によって、画面遷移が実行されます。
Twitterアプリでの投稿画面の表示や、メールアプリでのメール詳細の表示を例に見てみましょう。
Twitterアプリにおいて、話題を検索画面→投稿画面の画面遷移は
1. 話題を検索画面が投稿画面のひな形から実体を生成
2. 話題を検索画面が投稿画面の実体を表示
という流れで行われています。
メールアプリにおける、メール一覧画面→メール詳細画面の画面遷移は
1. メール一覧画面がメール詳細画面のひな形から実体を生成
2. コンテナ(UINavigationController)がメール詳細画面を表示
という流れで行われています。
アプリにおいては、「コンテナ」がUIの階層構造を強く規定し、画面遷移の実行なども行うため、使う「コンテナ」によって
・無理なくできること
・できるけど、しんどいこと
などが異なるようになっています。
全ての画面の基礎 ~UIViewController~
iOSアプリの画面遷移の仕組み、コンテナの概念を大まかに説明したので、早速個別のコンテナの解説に入りたいところですが、その前に、iOSの画面実装において全ての基礎となるUIViewControllerについて説明します。
基本的には、UIViewController = 画面(or その設計図) です。
こちら👇はApple StoreのTodayタブのスクリーンショットですが、赤線で囲まれた部分がUIViewControllerの実体になります。
UIViewControllerの役割は大きく2つです。
・画面の要素を表示する
・画面遷移の実行
例えば、Todayタブの内容を表示しているUIViewControllerでは、
・お気に入りやトレンドといった要素の表示
・それらの要素から次の画面への画面遷移
といった役割を担っています。
画面の切り替えを行うコンテナ ~UITabBarController~
UITabBarControllerは、TwitterやApp Storeをはじめとする多くのアプリで使われており、コンテナとしての役割を持つUIViewControllerの一種です。
以下のようなコードでUITabBarControllerを実体化し、実体化されたUITabBarControllerのviewControllers属性にいくつかのUIViewControllerの実体をセットすると、画面上部(画像青線内)にセットしたUIViewControllerのうち1つが、画面下部(画像赤線内)にUITabBarが表示されるようになります。
let viewController1: UIViewController = UIViewController()
let viewController2: UIViewController = UIViewController()
let viewController3: UIViewController = UIViewController()
let viewController4: UIViewController = UIViewController()
let tabBarController: UITabBarController = UITabBarController()
tabBarController.viewControllers = [viewController1,
viewController2,
viewController3,
viewController4]
UITabBarControllerでは、UITabBar内で選択中のタブを切り替えることにより、選択中のUIViewController(画像青線内)を切り替えることができます。
このように、UITabBarControllerはタブと紐付いた画面を切り替えて表示するのが主な目的ですので、instagramのように「UITabBar内のボタンを選択→新しい画面を表示」という画面遷移は割とトリッキーで「実現できるけどちょっとだけ面倒なこと」の1つだったりします。
階層を可視化するコンテナ ~UINavigationController~
UINavigationControllerは、メールアプリやApp Storeをはじめとする多くのアプリで使われており、コンテナとしての役割を持つUIViewControllerの一種です。
以下のようなコードで実体化されたUIViewControllerを使ってUINavigationControllerを実体化すると、画面上部(画像赤線内)にUINavigationBarが、画面下部(画像緑線内)にセットしたUIViewControllerが表示されます。
let viewController1: UIViewController = UIViewController()
let navigationController: UINavigationController = UINavigationController(rootViewController: viewController1)
そして、以下のようなコードを用いて2つめのUIViewControllerを実体化し、
UINavigationControllerに追加すると、ドリルダウンのアニメーションとともに、2つめの画面へ遷移することができます。
let viewController2: UIViewController = UIViewController()
navigationController.pushViewController(viewController2, animated: true)
このように、一覧→詳細といった構造をUI面で実現するために使われるのがUINavigationControllerです。
このことは、UINavigationControllerの公式ドキュメントでも、以下のように明記されています。
Use a navigation interface to mimic the organization of hierarchical data managed by your app.
(ナビゲーションUIは、あなたのアプリで管理される、階層的なデータ構造を真似て(表示するために)使いなさい)
一旦まとめ
ここまでの話をまとめると
・UITabBarController/UINavigationControllerで階層構造を定義しつつ
・実際の画面の描画はUIViewControllerが担当
・画面遷移はコンテナ/UIViewControllerのいずれかが担当する
といった内容になります。
UIView
上で「実際の画面の描画はUIViewControllerが担当」と書きましたが、
・テキストの表示
・ボタンの表示、ボタン操作の受け取り
・テキスト入力の受け取り
・スイッチの表示、スイッチ操作の受け取り
といった個々の要素の表示、ユーザー操作のハンドリングを行うのがUIViewとその派生系です。
(UIViewとその派生系)
・テキストの表示をするUILabel
・スイッチとして働くUISwitch
・ボタンとしてユーザー操作を取り回すUIButton
・1行のテキスト入力を受け取るUITextField
・音量調整などに使われるUISlider
・これらの要素をひとまとめにするために使われるUIView
(以下の画像では灰色の四角がUIView)
・画像の表示に使われるUIImageView
といったUIViewとその派生系を用いて画面を実装していくことになります。
気をつけるべきポイントは?
駆け足でしたが、ここまでで画面の階層構造と、個々の要素の表示を実際に行うUIViewについての解説が一通り終わりました。
それでは、UIデザインをする上でどういったところに気をつければ良いのでしょうか。
割と大丈夫なこと
・UIViewControllerの領域はだいたいどんなことでも大丈夫
先程も説明したように、UIViewControllerの表示領域(画像緑線内)では、UIViewやその派生系を用いて個々の要素を表示していきます。
そして、それらの実装についてはそれほど制約も多くないため、「そもそも原理的に不可能」といったものはあまり多くない気がします。
なので、データ構造と工数との相談にはなりますが、
・ある程度工数はかかるかもしれない
・保守がそこまでキツくなることはそれほど多くない
といった印象です。
実現可能だが、工数もかかり保守性が心配なところ
・UITabBar内の各タブのカスタマイズは面倒
UITabBarに含まれる各タブはUITabBarItemといい、実はUIViewの派生系ではありません。
そのため、Facebookの通知タブのようにタブ内の画像をアニメーションさせるのは通常の方法では実装することができず、
・iOSアップデートでも問題が出ないか
・画面が縦の場合と横の場合でレイアウト崩れが発生しないか
など考える必要があり、割と保守面で心配だったりします。
・UINavigationBar内の要素のカスタマイズも面倒なことが多い
UITabBar同様、UINavigationBarに含まれる要素もUIViewの派生ではないものが多く含まれています。
例えば、👇の画像の編集ボタンなどはUIBarButtonItemと言い、これもUIViewの派生系ではありません。
そのため、「NavigationBar上のボタンにバッジを表示したい」といったカスタマイズは割と面倒だったりします(実現できなくはないけど、別の問題が発生する可能性がある)。
・UITabBar/UINavigationBar/UIViewControllerのViewは異なるレイヤー
以下のような画面を持つアプリがあるとします。
一見、
・UINavigationBar(オレンジ)
・UITabBar(灰色)
・UIViewControllerのView(赤)は全て同じ階層にあるように見えますが、
iOSアプリの開発環境で見ると、このような階層構造になっています。
より抽象化して見てみましょう。
抽象化されたUIでレイヤー構造を示すと以下の通りです。
(ピンクはUITabBarControllerのViewを、緑はUINavigationControllerのViewを表している)
まず、UITabBarControllerのView(ピンク)とUITabBar(白)が存在し、その間にUITabBarControllerが内包するUINavigationControllerのView(緑)とUINavigationBar(オレンジ)が差し込まれます。
UINavigationControllerはUIViewControllerを内包しているので、UIViewControllerのView(赤)はUINavigationControllerのViewとUINavigationBarの間に挟まれます。
そして、ここからが重要なのですが、iOSのレイアウトロジックであるAutoLayoutは異なる階層に存在するレイヤーと関連付けたレイアウトがそこまで得意ではないため、「UINavigationBarとUITabBarから等距離となるようなUIViewのレイアウト」といったものは苦手だったりします。
まとめ
今回は「iOSアプリの画面の階層構造/画面描画の仕組み」「できること/できるけどしんどいこと」などを解説してみました。
これがデザイナー・ディレクター / iOSエンジニア間の相互理解の役に立ち、より良いデザインのアプリを作る助けになれば幸いです。
2020/02/18追記
本題とはズレますが、デザイナーさんとモバイルエンジニアの協業という点では以下の記事もオススメです(理想状態のデザインしか作られない方も割と多いかと思いますので)。
2020/11/29追記
iOSの画面遷移に的を絞った記事を、イラスト図解付きで追加しました。
iOSにおける自然な画面遷移と、その背景事情がわかるようになると、実装コスト・運用コストが高い不自然な画面遷移に気付けるようになり、開発が安定しやすくなるので、仕様策定を行うPM・ディレクターさんや画面デザインを行うデザイナーさんにオススメです。
参考
・UIViewController - UIKit | Apple Developer Documentation
・UITabBarController - UIKit | Apple Developer Documentation
・UINavigationController - UIKit | Apple Developer Documentation
・Views and Controls | Apple Developer Documentation
・UITabBarItem - UIKit | Apple Developer Documentation
サポートする代わりに個人開発はじめましょ! iOS👇 https://developer.apple.com/jp/support/enrollment/ Android👇 https://play.google.com/apps/publish/signup/