読者です 読者をやめる 読者になる 読者になる

Akihiro's Programmer Blog

Technology Notes for Personal

iOSアプリのレイアウト事始め

Advent Calendar iOS
この記事は、モバイルファクトリー Advent Calendar 2015 13日目の記事です。

昨日は @nekobatoさんの 「style-loaderを使ってstylesheetをrequireする」 でした。今日はiosアプリ開発をする際のレイアウト作成に欠かせない要素である、Autolayoutについて実作業を交えてお話できればと思います。


目次


準備

iosアプリを開発する際に、実際にアプリのレイアウトを設計するための GUI として、Xcode には xibstoryboard があります。

実際に表示する画面に対して UIView や、UILabel と言ったパーツをドラッグ&ドロップで配置していくことでレイアウトをしていきます。


物は試しと言いますし、実際にやってみましょう。


Xcode を開いて新規プロジェクト作成。

f:id:akihiro_0228:20151212170455p:plain:w300


Sigle Page Applicationを選択して、

f:id:akihiro_0228:20151212170709p:plain:w300


プロジェクト名は適当に sample

f:id:akihiro_0228:20151212170729p:plain:w300


Xcode の左ペインに表示されているファイルツリーから、 Main.storyboard をクリック。

f:id:akihiro_0228:20151212170843p:plain:w300


今回は分かりやすいように iPhone 5/5s/5c の画面サイズを起点に考えるため、 中央ペインの左側にある Viewツリー から View Controller を選択して、

f:id:akihiro_0228:20151212170853p:plain:w300


右ペインを下画像のように設定します。

f:id:akihiro_0228:20151212170914p:plain:w300


これで View Contoller (ここでは端末の画面だと思っていただいて大丈夫です) のサイズが iPhone 5/5s/5c 仕様になりました。準備OKです。

f:id:akihiro_0228:20151212170931p:plain:w300


レイアウト

いよいよレイアウトしていきます。右下ペインから、配置したいパーツを検索して、ドラッグ&ドロップで配置していきます。今回は UIView というパーツを配置してみます。


右下ペインから UIView を検索。

f:id:akihiro_0228:20151212171110p:plain:w300


表示された View を中央ペインの View Controllerドラッグ&ドロップ

そのままだと配置した UIView が分かりにくいので、サイズや色を調整します。

f:id:akihiro_0228:20151212171219p:plain:w180f:id:akihiro_0228:20151212171224p:plain:w180


これでレイアウト作業完了です!

後は Xcode 左上から iPhone 5/5s/5c のどれかのシミュレーターを選んで、 run ボタンを押して、実際に Simurator で動作確認してみましょう。

f:id:akihiro_0228:20151212171306p:plain:w300


OK!

f:id:akihiro_0228:20151212171317p:plain:w300


実は終わりじゃない

できたできた。なーんだ、かんたんじゃーん。 と思っているそこのあなた。

実は終わりじゃないのです。このままだと問題ありです。


試しに別のシミュレーターで起動してみます。

f:id:akihiro_0228:20151212171340p:plain:w300


iPhone 5/5s/5c の画面サイズだと中央に表示されていましたが、 iPhone 6 だと少しズレた所に表示されてしまっています。。

これは困った。


理想としては、以下の様なレイアウトになっていてほしいのです。

f:id:akihiro_0228:20151212174316p:plain:w300


Autolayout

ここで登場するのが、iOS6 から導入されたレイアウトシステムである Autolayout です。

Autolayout は各Viewに対して「制約」(Constraint)と呼ばれる約束事を指定することで、他Viewのサイズ変更に追従する形で位置やサイズを決定してくれる仕組みです。

それ以前のレイアウトシステムとして Autosizing というものがありましたが、Autolayout の登場によってさらに細かいレイアウト指定が可能になりました。

今回は、この Autolayout を使って、理想としていたレイアウトを実現してみましょう。


まずは配置したViewの位置を指定してあげましょう。 位置は画面の中央に配置したいので、そのように制約をかけます。


配置したViewをクリックして選択した後、中央ペイン右下の Align ボタンを押します。

f:id:akihiro_0228:20151212171419p:plain:w300


すると、他Viewとの位置揃えに関する制約をかけることが出来るAlignメニューが表示されます。
今回は、「親Viewに対して、中央揃えにしたい」ので、Horizontal Center in ContainerVertical Center in Container のチェックを付けて、 Add Constraints ボタンで制約を追加します。

f:id:akihiro_0228:20151212171426p:plain:w300


これで iPhone 6 のシミュレーターで実行してみると中央表示になっていることが分かります。

f:id:akihiro_0228:20151212171440p:plain:w300


ですが、これだけだと実は問題があります。

中央ペインの左側にある Viewツリー に赤い矢印のアイコンが表示されています。これは制約が足りない、もしくは制約に矛盾が生じているためのエラーで、実行時に正しいレイアウトになってくれない危険性があることを示しています。

(build出来ないということはないので、今回のように偶然期待通りのレイアウトになってくれることもあります)

f:id:akihiro_0228:20151212171450p:plain:w300


アイコンをクリックしてみると、実際のエラー内容を見ることが出来ます。
エラーは2つ。Need constrains for: X position or widthNeed constrains for: Y position or height

つまり、指定ViewのX座標もしくは横幅、指定ViewのY座標もしくは縦幅を確定出来るような制約をかけてね、ということです。

f:id:akihiro_0228:20151212171455p:plain:w300


つまりは、「このままだと確かに中央に表示されるけど、Viewがどんな形になるか分かんないよ。いいの?」って言ってくれてるわけです。親切ですね。

良くないので、Viewの形もしっかり確定させてあげます。

理想としては160x160の正方形になっていてほしいので、そのように制約をかけてみましょう。


また配置したViewを選択した後、Align ボタンの隣りにある Pin ボタンを押します。

f:id:akihiro_0228:20151212171536p:plain:w300


WidthHeight を160に指定して、チェックした後 Add Constraints ボタンで制約を追加します。

f:id:akihiro_0228:20151212171543p:plain:w300


これで先ほどあったエラーアイコンが消えて、画面から不安な赤色が消えました。
この状態が、適切に制約をかけられている状態になります。

f:id:akihiro_0228:20151212171552p:plain:w300


これでどの画面サイズに対しても、「中央表示で160x160固定」なViewが表示されるようになりました。やったね。


制約の指定で気をつけること

慣れない内は制約を指定してもエラーや警告が消えずに四苦八苦すると思います。(僕がそうだったので)

なので、僕が制約の指定に慣れることが出来た考え方を紹介できればなと思います。


制約の指定は全てのViewを配置し終わってから一気にやる

指定された制約は、Viewの位置・サイズの変化等によって、追従して勝手に変わることが多々あります。

つまり、Viewを配置しながら制約も一緒に指定しているときに、「あ、このViewのサイズはこうじゃないや」とサイズ変更すると、今まで指定してきた制約が警告を発し始め、アワアワしてしまいます。その後の一つ一つ制約の警告を消していく作業が面倒くさい。

Update frames や、 Update constraints によって自動で警告を消すように調整してくれる機能はありますが、把握していない箇所で意図しない変更が加えられる可能性があるので基本使わないようにしています。

また、Viewの階層を変更すると、そのViewのみに指定された制約はそのままに、そのViewと以前制約をかけあっていたViewの制約は消えてしまいます。

つまり、Viewを配置しながら制約も一緒に指定しているときに、「あ、このViewの階層はここじゃないや」と階層を変更すると、(ry

要するに、警告解消作業が面倒くさいので、制約の指定は全てのViewを配置し終わってViewのサイズや位置を変更しないようにしてから一気にやるのが安全性が高いです。


指定の順番は親Viewから順に

Viewの制約は、基本的に自身か親View、もしくは同階層のViewに対してかけることが多いです。

なので、一番上の階層にあるViewから順番に制約をかけていくと、エラーや警告に怒られることなく制約追加を進めることが出来ます。

また、そうすることでそのViewを他の箇所にも使いたいと思った時に、最上位のViewのみ制約が壊れるので、そのViewのみエラー/警告を解消してやればコピペしやすくなります。


「位置」と「形」の可能性を潰す

なにも制約を指定していない状態というのは、「どんな位置にもどんな形にもなる可能性のあるView」であることが言えます。そんなものはレイアウトしているとは言えません。

なので、まず「位置」の可能性を潰します。

今回のように水平方向と垂直方向の中心になるような制約を指定してあげれば、そのViewが存在しうるのは画面中央しかあり得なくなります。

次に「形」の可能性を潰します。

このままでは、ものすごく縦長のViewでも横長のViewでも制約をクリアしてしまうので、縦幅と横幅を160で固定してしまいました。

そうすることで、そのViewの形は160x160の正方形でしかあり得なくなります。

こういう具合で、制約を追加する時は、そのViewの「位置」と「形」が1つの可能性に収束するかどうかを見るようにしています。


終わりに

Autolayout についての解説記事は多くありますが、今回はプロジェクト作成からレイアウトまで、順番に一連の流れを簡単にですが解説させていただきました。

実際のアプリ開発では今回のようなシンプルなUI構成ではないことが多いですが、基本的にレイアウトの作業は今回のサイクルの繰り返しになります。なので、今回のような手順を自然に出来るようになれば、iosアプリのレイアウト初級には入門出来たのではないでしょうか。

次回は @pine613さんの 「Kotlinの拡張関数について」 です!