iOSアプリを作ってみる (8) - Autolayoutに振り回される その1

これもハマったという意味で記事にしておきたいと思います。

アプリを作っているときには、iPhone5の縦長ディスプレイ(Retina 4inch, 解像度640*1136、ポイント320*568)をベースに作っていました。実際に、シミュレーターなり、実機(iOS7 beta)で動かしている分にはこんな感じで、とりあえず自分の意図した形で各部品が配置されていました。

f:id:deutschina:20130916085811j:plain

しかし、これを3.5inchのシュミレーターで動かしてみると、

f:id:deutschina:20130916085820p:plain

なんじゃこりゃ?ここから苦闘(!?)の始まりです。

(注:字が一部違うのはレイアウトのせいではありませんw)

意味不明だよAutolayout

自分にとって最初のアプリ開発であること、またアプリ自体がシンプル極まりなかったので、あまりトリッキーなことはしないで、できるだけ楽をさせてもらおうと決めていました。したがって、StoryboardにAutolayoutという組み合わせで開発を進めていた訳です。

そのときに、Powerpointの分品の整列や均等配列よろしく、自動でいろいろ調整してくれそうなメニューがあったので、意味も分からずバンバン使っていました。

f:id:deutschina:20130916090507p:plain

いやはや、これが間違いの始まりだった。このクソ機能はしばらく忘れておいた方が良いと今でも思っています。(苦笑)

とにかくレイアウトが壊れた原因を調べようと、Storyboardで画面を開いてみると、もうこんな感じ。

f:id:deutschina:20130916090932p:plain

この赤いのがなんだかイケないというのは分かりますが、それにしても何から手を付けて良いのか分かりません。

とりあえずいらんモノは消してみよう

今回のアプリは、iPhoneで使う事しか想定していませんし、横倒しに対応もしていません。従って、気をつけないと行けないのは、4inchと3.5inchの長さの違う2つの画面に対応すると言うことだけです。また画面サイズ的には88ポイント差(568-480=88)しかないので、個々の部品(ラベルなど)を3.5inchの場合に小さく表示するのではなく、各部品の間隔を詰めてやれば十分対応可能なはずと考えました。

f:id:deutschina:20130916091503p:plain

この画面右側の紫色のアイコンが、ユーザ定義のConstraintsなのですが、先ほどの真っ赤な画面の時は、これの数がこの画面の倍以上ありました。同じ部品から複数のConstraintsが伸びていてもう訳が分からなかったので、いらなそうなモノをバンバン消していくことから始めました。いっそのこと(Constraintsを)全部消して最初からやり直しても良いかも知れませんね。

ちなみにConstraintsを選択した状態でDeleteキーを押すだけです。間違って消しても気にしないでください。もう1回設定すれば良いだけの話です。

いらなそうなモノを消した後で、とりあえずConstraintsとして必要なものは、こんなもんだろうと定義しました。

  • 直下、直上の部品との距離 (vertical spaces)
  • 真ん中にあるべき部品のセンター揃え
  • 真ん中にない部品の画面左右端(どちらか近い方)からの距離 (holizonal spaces)
  • 最上部と最下部の部品の画面上下端からの距離 (vertical spaces)
  • 各部品の高さと幅

これを1つ1つ丁寧に指定してPINを刺していくことにしました。(Menu: Editor->Pin)

2つの画面サイズに対応する方法

先ほども書きましたが、今回の場合は考慮すべきは高さの違う2つの種類の画面に対応することです。特に、今回の場合は部品の大きさは原則として変更せずに、部品と部品の間を旨く調整するのがポイントになります。

これをどのように実現するのかという方法については、ネット上にもいろいろ議論がありました。例えば、この手のトラブルシューティング系の超有名サイト(stackoverflow.com)には、ダミーのラベルなどを部品と部品の間に配置するという方法が紹介されています。確かに、各部品には同じ高さ(same height)にそろえるconstraintsが用意されているので、それらのダミーラベルの高さをそろえるイコール部品が均等に配置されるというのも一理あります。ただ、これだとただでさえ乱雑とした画面がさらに乱雑になるような気がして、個人的には気乗りしませんでした。

ただ、これらの議論を見ていて1つ気がついたのは、例えばとあるラベルともう1つのラベルの間のVertical spacesを設定するためのConstraintsは1つに限らないのです。また、数値の設定もequal toだけではなく、less than, greater thanも指定できます。つまり、2つのConstraintsを設定して、うち1つをless thanで指定、もう1つをgreater thanと指定すると、それらがこの場所におけるvertical spacesの最大値と最小値となります。これをうまく使ってやれば、だいたい想定したとおりの部品配置とすることが出来そうです。

f:id:deutschina:20130916161214p:plain

less / greater thanを使うとこんな感じで不等号マークが表示される。

ここからが面倒だった

しかし、ここからが面倒でした。範囲指定をしたとは言え、目的はすべての部品を画面の中に納めるだけではなく、きちんと整列した状態に配置することにあります。2つのconstraintsで部品の間隔を範囲指定やることは出来ますが、そこを適当にやると、1カ所だけ極端に詰まって、他のところは間延びした状態というように美しくありません。

そこで登場したのが、紙とペン(笑)。

笑うことなかれ。これが大事なんです。まずは3.5インチ相当と4インチ相当の長方形を書いて、そこに部品を適当に配置します。今度は、部品と部品の間隔のうち、この2つに分類します。

  • 常に固定
  • 多少の変動があってもOK

常に固定というのは、どちらの画面サイズでも大きさを変えないもの。具体的にはラベルやボタンの高さなどは変更したくないのでこちらに分類します。もう1つの「多少の変動OK」というのは、いわゆる部品と部品の間など、多少サイズが変わっても影響しないだろう部分です。あとは、画面のポイント数から常に固定部分を引いてやると、それぞれの画面サイズにおける変動部分の合計が出てきます。

  • 3.5インチ : 480 - (常に固定分) = 変動OK部分の合計(A)
  • 4 インチ : 568 - (常に固定分) = 変動OK部分の合計(B)

画面の有効長さが88ポイント違うので、(A)と(B)の差は必ず88になります。

例を出して説明すると、部品が5個ならんでいて、それらを均等に並べるとしましょう。つまりVertical spacesを4カ所に設定します。(A)の値は44、(B)の値は132だとすると、1つあたりのvertical spacesの値は、以下のようになります。

  • 3.5インチ : 44 / 4 = 11
  • 4 インチ : 132 / 4 = 33

この値が出たら、実際にvertical spacesの設定に行きます。それぞれ2つの部品を選択して、Menu: Edit -> Pin -> Vertical spacesと選択して、まずは"Greater than or equal to" の値として11を指定、さらに同じ設定をもう1回選択して"Less than or equal to"の値として33を指定してやります。

これをやることによって、最小値を拾って、なおかつ固定部分を足した合計が480、また最大値を拾って固定値を追加した値が568になるように調整できます。このようにしておけば、3.5インチでも4インチでも、ひとまず想定通りに部品が配置されるはずです。ちょっと見てみましょう。

f:id:deutschina:20130916164745p:plain

はい、部品がきれいに整列されているのが分かると思います。

めでたしめでたしと言いたいところだったのですが、さらなる問題が発生しました。ただ、長くなったので次の記事に書こうと思います。