【iOS】iOSアプリ開発入門~ レイアウト編２~

前回はStoryBoard上にボタンを配置する方法、ボタンの色変更方法について投稿しました。
レイアウト編１：https://qiita.com/euJcIKfcqwnzDui/items/ba37adae94e07c89d404

今回はUIの配置やサイズの調整方法について解説していきます。

## AutoLayoutとは
iOSアプリのUIの配置、サイズ指定には必ずといっていいほど**AutoLayout**というXcodeならではの概念を使う必要があります。

ではAutoLayoutとは何でしょうか。
iPhone、iPadのを想像してもらえるとわかりますが機種によって画面の大きさが異なります。
参考：https://qiita.com/tomohisaota/items/f8857d01f328e34fb551

この多岐にわたる画面サイズに適応させるための仕組みをAutoLayoutと言います。

何を言っているかイメージしづらいかと思います。
AutoLayoutで組んだレイアウトを一度シミュレータで見てみましょう。
[前回](https://qiita.com/euJcIKfcqwnzDui/items/ba37adae94e07c89d404)使用したプロジェクトでMain.storyboardを開いてください。

まず差をわかりやすくするためにstoryboard上でのViewサイズを変更します。
(Viewとは今白く表示されている端末画面のような部分ととりあえず認識してもらえればいいです。)
storyboardの下の方にある[View as:iPhone〜〜]となっている部分を選択し[Device]を選択してください。
<img width="1005" alt="スクリーンショット 2020-05-17 20.30.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/459ec4c0-4b55-43c3-4987-3d0e35bf31ec.png">

すると端末の種類をプルダウン形式で選択できます。
これはstoryboard上でViewサイズを変更するメニューです。
今回は[iPhone 11 Pro]を選択してください。ViewのサイズがiPhone 11 Proのサイズになります。

次に以下のように配置されているボタンをViewの右上に配置してください。
ドラッグアンドドロップで移動させることができます。
<img width="1180" alt="スクリーンショット 2020-05-17 20.39.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/fd5706dc-6995-c29e-4eb1-48ebb8a4b654.png">

この状態でiPhone 11 Proのシミュレータで実行します。
(シミュレータの選択方法は[以前](https://qiita.com/euJcIKfcqwnzDui/items/0bbde21f63825cb0cd88)説明しているので参照してください。)
ボタンの位置はどうでしょう？
storyboardと同じ位置に配置されています。
<img width="448" alt="スクリーンショット 2020-05-17 21.19.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/1e0584c0-de8d-6db7-8f6a-d6b9466a4a47.png">


今度はこのままiPhone 11 Pro Maxのシミュレータで実行してください。
どうでしょうか？
<img width="496" alt="スクリーンショット 2020-05-17 20.48.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/3bd0b6f0-ad03-be1e-b7f5-db5c7533c564.png">

ボタン右側に余白ができてしまったかと思います。
原因はiPhone 11 ProとiPhone 11 Pro Maxの端末サイズが異なるためです。

iOSの座標系は画面の左上を原点として取り、右方向にX軸の正、下方向にY軸の正を取ります。
またその単位はdpです。
この座標の1dpあたりの大きさは端末毎に変わらず一定です。
AutoLayoutを使用しなければUIは配置された座標に表示されます。
この差により右側に余白ができてしまいます。
<img width="728" alt="スクリーンショット 2020-05-17 21.15.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/d4b26459-2b32-e0ec-68ba-7efb21736957.png">

この差分を吸収するためにAutoLayoutを使用します。

## AutoLayoutを設定する
それではAutoLayoutを使って右上にボタンを配置していきます。

ボタンを選択した状態で下の方にあるアイコンのうち真ん中のアイコンを選択します。
先程Viewサイズを変更したあたりの右の方です。
<img width="721" alt="スクリーンショット 2020-05-17 21.39.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/8ac992a8-45c9-2718-cc92-787a0f4e4500.png">

選択するとメニューが表示されます。
まずメニューの一番上に上下左右に数字が出ています。
これは上下左右にどれだけのマージンを取るかという設定です。
とりあえず右上に寄せたいので上と右に0と入力します。
真ん中あたりのマークが濃い赤にならなければマークをクリックして濃い赤に変えてください。
その後[Add 2 Constraints]をクリックします。
するとボタンの上と右に0dpのマージンが設定されます。
このようなUI設定するマージン等のことを**制約(Constraint)**と呼びます。
<img width="270" alt="スクリーンショット 2020-05-17 21.51.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/e7992d17-873c-3b81-78f9-c5f4b4f82123.png">

それではこの状態でiPhone 11 Pro、iPhone 11 Pro Maxでそれぞれ実行してください。
機種に依存せずボタンが右上に表示になったかと思います。

この追加した制約はUIのsrotyboardに追加されています。
<img width="1170" alt="スクリーンショット 2020-05-17 22.06.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/3c549413-38b7-9489-b2d1-e2b86f817c8d.png">

画面左の階層構造に`Button.top = Safe Area.top`とあります。
Safe Areaは少し難しいかもしれないのでとりあえずは大元となるビュー、画面自体というように認識しておいてください。
Buttonというのは配置しているボタンのことです。ボタンの名前を変えてあげると自動的に反映されます。
topというのはそのまま上部という意味です。
つまりこの制約は「Safe Areaとボタンの上部が同じ位置にある」ということを示します。
【補足】

- UIのリネーム方法：UIを選択した状態でもう一度クリックする
- 制約の意味
    - top：上
    - bottom：下
    - leading：左
    - trailing：右

設定したマージンを変更することもできます。一度試してみましょう。
ボタンのtopマージンを選択するとInspectorにtopマージンの設定が表示されます。
Attribute InspectorのConstraintの値が0になっています。
これが現在設定しているtopマージンの値です。
<img width="261" alt="スクリーンショット 2020-05-18 1.37.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/596df387-c918-b4d3-ca0e-f573c5ed49b4.png">

これを100に変更してみます。
topマージンに100dpが設定され、ボタンが100dp下方向に動いたかと思います。

このようにして制約を追加し、端末間の差分をなくすための機能がAuto Layoutです。

## 複数のUIを配置しにマージンを設定する
実際のアプリではこのようなボタン１つではなく様々なUIが配置され、その分制約が増えAuto Layoutの設定は複雑になります。
こればかりは慣れるしかありません。
一度練習としてもう一つUIを配置してみます。

Text Fieldを追加します。
（このテキストフィールドはキーボードから文字を入力するためのUIです）

[ボタンを追加したとき](https://qiita.com/euJcIKfcqwnzDui/items/ba37adae94e07c89d404#%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%92%E9%85%8D%E7%BD%AE%E3%81%99%E3%82%8B)のように[+]ボタンからUIのメニューを開きます。
[Text Field]を選択しビューの適当な位置にドラッグアンドドロップします。
するとビューにText Fieldが追加されました。
<img width="1178" alt="スクリーンショット 2020-05-18 1.54.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/604105e1-0198-197e-a17b-6fb64082daa2.png">

説明した通りこの状態では端末毎に位置が異なります。
制約を追加してあげましょう。
今回はボタンと同じ高さに左寄せで配置してあげます。
同様の手順でテキストフィールドを選択し、leadingマージン:0, topマージン:100と設定してあげます。
<img width="270" alt="スクリーンショット 2020-05-18 2.01.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/2efaf782-ae29-6546-a99f-bdd4563b1588.png">

これでボタンと同じ高さにテキストフィールドを配置することができました。
ですが配置されたテキストフィールドを見ると小さくなっていませんか？
制約を追加すると自動的にサイズ調整がされるようです。
それではサイズの制約を追加してあげましょう。
制約追加のメニューに[Width]、[Height]の項目があります。
これらにチェックを入れ任意のサイズを設定します。
<img width="277" alt="スクリーンショット 2020-05-18 2.07.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/bfbc5c48-3f80-6b1c-4354-007f67199e7f.png">

これでテキストフィールドのサイズ指定ができました。
もちろん同様の方法でボタン等他のUIでもサイズの制約を追加することができます。

これで複数のUIを追加することができましたが、考えてみてください。
今回テキストフィールドの位置はボタンの位置と合わせるためtopマージンに100を設定しました。
ではもしボタンの位置を調整したいとなったときテキストフィールドのtopマージンも合わせてあげる必要があります。
今は２つなのでそれほど問題にはなりませんが、それが5個、10個となるとそれだけ手間になりますし修正漏れが発生する可能性があります。

そこでテキストフィールドの高さを相対的に指定してあげます。

一度テキストフィールドのtopマージンを削除してください。topマージンを選択しキーボードからバックスペースを入力するだけです。
下図のようにテキストフィールドが赤くなっていればOKです。
余談ですがこの赤くなっている状態はAuto Layoutのエラーです。話が逸れるので次項で簡単に説明します。
<img width="910" alt="スクリーンショット 2020-05-18 2.22.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/c54f27a1-3565-aa8d-09b2-8dafbf733783.png">

次にcommandキーを押しながらボタンとテキストフィールドを選択します。
<img width="803" alt="スクリーンショット 2020-05-18 2.27.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/14f86e49-f24a-c4b7-77e1-1eca0de70c8f.png">

下の方にあるタブの右から４つ目を選択します。(先程制約を追加する際に選択したタブの左側)
<img width="334" alt="スクリーンショット 2020-05-18 2.28.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/c20a3c7c-9356-5f5f-a914-f0f42fcf55a0.png">

このメニューからは選択した２つ以上のUIに相対的な制約を追加することができます。
今回はtopマージンを相対的に指定したいので[Top Edges]にチェックを入れ、0を入力し[Add 1 Constraint]を選択します。
これは選択された複数のUI間のtop間の差は0dpです、という制約です。
<img width="305" alt="スクリーンショット 2020-05-18 2.34.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/2c64652d-b651-4e92-0539-0a06804d24bb.png">

追加するとAuto Layoutのエラーが消えボタンとテキストフィールドのtopが等しくなりました。
<img width="869" alt="スクリーンショット 2020-05-18 2.38.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/ff6cf4ee-1d72-7878-4c28-0c86bb335bbd.png">

ではこれで本当に相対的な制約を追加できたのか確認します。
前項で説明した手順でボタンのtopマージンを変更してください。

どうですか？
一緒にテキストフィールドの位置も変わったのではないでしょうか？

このようにして複数のUI間に制約を与えていき複雑なUIを組み上げていきます。
そのためそれぞれのUIが影響しあい、１つでも制約を間違えていると大きくレイアウトが崩れたりエラーが発生したりします。
本項冒頭でも言いましたがこれは慣れていくしかありません。
トライアンドエラーで頑張ってください。

## Auto Layoutのエラー
前項の途中でAuto Layoutのエラーが発生しました。
これについて簡単に解説します。
<img width="803" alt="スクリーンショット 2020-05-18 2.27.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/ada3794b-db55-c068-0c26-6905a308ed7b.png">

このエラーはtopマージンを削除したときに発生しました。
上の方に赤い矢印が出ていますがこれをクリックするとエラーの内容を確認できます。
エラー内容は以下です。
<img width="418" alt="スクリーンショット 2020-05-18 2.56.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326173/b2cc2ffd-4a2b-e493-3c9c-ced4aea9b79a.png">
Need constraints for: Y position
Y座標に対する制約が必要ですよとのこと。

Auto Layoutでは１つでも制約を追加すると完璧に位置を決定できるように制約を追加してあげる必要があります。
削除前はビューからマージンとしてtop：100dp、leading:0dpと指定していました。
その状態だと(x,y) = (0, 100)の位置に表示すればいい、ということがInterface Builderが認識してくれます。
ですが削除すると(x,y) = (0,?)という状態になりInterface Builderが理解できなくなったためエラーが発生しました。

その後ボタンとテキストフィールドの間に制約を設定したため
ボタンとビューのtopマージン:100 
=> ボタンとテキストフィールドのY座標の差:0 
=> テキストフィールドのY座標:100
という流れでInterface Builderが理解できるようになりエラーがなくなったというわけです。

エラーが発生したら足りてない制約はないか？どうすればInterface Builderが計算できるようになるかを考えていく必要があります。

制約がよくわからなくなってしまったら、追加した制約を全て選択してキーボードのバックスペースを押すと消せるので一度全て消してあげても大丈夫です。

## 最後に
今回は端末間の表示差をなくすためのAuto Layoutについて説明しました。
何度か言ってますがこれは習うより慣れろです。筋トレのようなものだと思って諦めてください。
Auto Layoutはアプリ開発の第一関門です。覚えてしまえば格段に楽になります。

とはいえAuto Layoutの内容は量が多く、この連載だけでは説明しきれない部分が多くあります。
本を買って勉強するなりして身につけていきましょう。

以上で今回の説明は終わりです。
次回はUIとSwiftの接続方法について説明してます。
SwiftとUIの接続編１：https://qiita.com/euJcIKfcqwnzDui/items/f794ee8b996e6d650027

本連載ではプログラミング未経験からiOSアプリ開発が行えるようになることを目的としています。
今までの投稿をまとめていますのでこちらもご覧ください。
http://naoyalog.com/