NSBlogger

意識高いブログ

AutoLayoutとうまく付き合うコツ

AutoLayoutと仲良くなった

ぜんぜん言うこと聞かないからAutoLayout大嫌いだったんですが、接し方を変えたら言うこと聞くようになったので、そのコツを紹介します。

AutoLayoutにふりまわされないように

AutoLayoutを使うと、色んな画面サイズに柔軟に対応することができます。今まではAutoresizingmaskを使っていましたが、AutoLayoutが主流になりつつあるので、積極的に使っていきたいです。
しかし、AutoLayoutを初めて触ったとき、なんで思い通りにならないんだ!と何度も悔しい思いをしたことがあります。挙句の果てには、見返してもよくわからない制約がいろんな場所についてしまって、しぶしぶ「Use AutoLayout」のチェックをはずしてリセットすることもありました。
初歩的なことですが、以下で紹介することを念頭においてAutoLayoutを設定すると、悲惨な目に遭いにくくなるのでぜひご参考ください。

AutoLayoutを設定する下準備

完成後のレイアウトを作る

まずは下準備として、どんなレイアウトを作りたいかのイメージを用意しましょう。たとえば以下のようなデザインをAutoLayoutを使って作るとします。
f:id:Kamekiti:20150314191605j:plain
完成後のレイアウトがあるとXIB上での作業がしやすくなります。基本的にはXIB上でこのレイアウトをそのまま描くことになります。

各要素の幅や高さ、余白を定義する

AutoLayoutで制約をつけ始める前に、幅や高さや余白について定義しておく必要があります。
f:id:Kamekiti:20150314191837p:plain
ぜひ定義しておきたい項目は以下です。

  1. 各要素の余白(マージン、パディング)
  2. 各要素の幅と高さ
  3. 余白や幅・高さは定義しておかないと、AutoLayoutの制約を設定しているときに混乱します。不足があると仮の値を入れないといけなかったりするので、必ずAutoLayoutを設定する前に定義しておきましょう。固定のサイズではなく、「最低10px以上」といった指定でもOK。
  4. フォントの大きさ
  5. こちらも必須で、フォントの大きさによってUILabelの大きさも調整する必要がでてきます。フォントの大きさもあらかじめ決めておくとよいです。
  6. 各要素の比率
  7. 画面サイズによって要素の大きさを動的に変える場合に必要となります。たとえば、「サムネイルは画面幅の50%」といった制約です。
  8. 優先すべき設定
  9. 画面サイズが変わったときや要素をhiddenにしたときの対応で必要になることがあります。AutoLayoutの設定でいう「Priority」がそれに当たります。 ※iOS9からUIStackViewが使えるようになりました。こちらを使うとhidden対応が楽ちんになります。

AutoLayoutを設定するコツ

以下を順番に行うと、AutoLayoutでの事故を防ぎやすくなります。

レイアウトの見本通りにXIBを作成

まずは、レイアウトをみてその通りにXIB上にパーツを配置しましょう。幅や高さ、余白もレイアウト通りに設定します。
f:id:Kamekiti:20150315013640p:plain
でたらめな値で設定したり、表示すべき要素が足りない状態で、AutoLayoutを設定し始めると泣きをみます。とくに要素の配置は重要で、要素Aの隣に要素Bがあるはずなのに、XIB上でそれが正しく反映されていない場合、(当然ですが)制約をうまく設定することができません。制約をつける際の事故をなるべく防ぐためにも、レイアウト通りに正確に要素を配置しておくことは非常に重要です。

順番に制約をつける

制約をつけていく順番は大事です。SuperViewに触れるものから順番につけていくとよいです。いきなり真ん中あたりに表示されている要素に制約をつけると必ずエラーが発生します。というのも、まだ制約が設けられていない要素との間に制約をつけようとするからです。配置が未確定のものをベースに制約をつけても、その制約は未確定のままでエラーが表示されます。たとえば上記の例だと、最初に経路案内のViewに制約をつけるのは間違いです。
f:id:Kamekiti:20150315023310p:plain
まずは美ら海水族館のUIImageViewから制約をつけ始める方がよいです。次は地図部分、その後に「沖縄美ら海水族館」のUILabelといった流れでしょうか。制約が確定しているものとの間に新しい制約をつけていくとエラーにならないので、スムーズにAutoLayoutの作業が進みます。エラーがでても、その都度解消すればよいのですが、初心者にとってはそこで混乱しがちです。少しでもスムーズに進めるためには、制約をつける順番はとても大事なのです。

必ずUpdate Frameをしながら、ひとつずつ制約をつける

レイアウトが完成したら、制約をつけていきます。制約をつけることで様々な画面サイズに対応することができます。
制約の付け方はたくさんありますが、覚えておきたい基本的な制約は以下です。

  1. 幅や高さを固定する

  2. 「Width」「Height」に固定値を入力します。

  3. 各要素の周囲に余白を設定

  4. エ←こういう記号をクリックして有効化したあと、数値を入力します。マージンが設定できます。

  5. 中央に配置させる

  6. 水平方向、垂直方向に中央寄せすることができます。

そして、重要な点は各要素ごとにひと通り設定をしてから次の要素の設定へ移るということです。手当たりしだいに色んな要素に制約をつけるのは混乱を招きます。自分でもどこに何を設定したかわけがわからなくなります。その結果、制約のエラーだらけになり収拾がつかなくなり、リセットするはめになります。こうならないためにも、必ずひとつの要素内の制約をすべてつけ終わってから次の要素へ移りましょう。


f:id:Kamekiti:20150315023740p:plain
もう一つ重要なことがあります。制約をつけ終わったら、次の要素へ移る前に必ずUpdate Frameをしましょう。Update Frameをすると、制約を加味したレイアウト配置をXIBに反映してくれます。制約をいくつか付けると、要素の配置が最初の設定から少しずれることがあります。これは仕方がないことなのですが、このままXIBのレイアウトに反映せずに次の要素に制約をつけると、警告だらけになります。そして最後にそれらをまとめて解消しようとすると、事故ることがあります。事故を防ぐためにも、ひとつの要素に制約を付け終わったら必ずUpdateFrameをしてXIB上のレイアウトを更新しておきましょう。Update Frameをこまめにやる癖をつけていると、思わぬ事故に遭いにくくなると思います。ちなみに、Update Frameした後の配置は、XIB上に点線であらかじめ表示されています。自分の予想しているレイアウトと大きく異なる場合は、制約の設定が間違っている可能性があるので、事前にチェックしておきましょう。

AutoLayoutで覚えておくと便利な制約の付け方

上記の3点に気をつければ、AutoLayoutでの事故は格段に減ると思います。あとは制約の付け方のテクニックを知っているかどうかになります。以下に覚えておくと便利な制約の付け方を紹介します。

  1. 幅や高さを可変にする
  2. UILabelでよく利用します。テキストの長さに応じてUILabelの長さを自動調整したいとき。「最低10pxはほしいけど、あとはテキストの長さに応じて可変にしたい」といった場合、下記のメニューから設定できます。

    最初に固定幅で指定しておき、その後メニューから「≧」を選びます。すると、UILabelの大きさが指定した大きさ以上に自動調整されます。便利!
  3. 画面幅に応じて要素の幅を変える
  4. たとえば、画面サイズの50%の大きさにしたいとき。2つの要素を選択して制約をつけることで実現できます。

    ダミーのViewと美ら海水族館のUIImageViewを選択しています。ダミーのViewはSuperViewと同じ大きさのものを置いています。SuperViewとUIImageViewを選べばよいのかなと思ったのですが、なぜかうまくいかなかったので、ダミーを作りました…。そしてそのまま制約の設定へ。「Equal Widths」を選択しましょう。すると選んだ2つのViewの横幅が同じになります。

    今回は50%の大きさにしたいので、制約を編集します。「Multiplier」に「1:0.5」と記述するとUIImageViewが全体の大きさに対して50%の大きさに自動調整されるようになります。XIB上の制約には=マークが表示されます。
  5. 横幅と高さを同じにしたい
  6. ひとつの要素内でAspect比を設定できます。

    Aspect Ratio」から設定できます。こちらも先ほどと同様に「Multiplier」で比率の調整が可能です。
  7. 要素をHiddenにしたときに詰める
  8. AutoLayoutの「Priority」を使って、場合に応じてUILabelの幅をゼロにしてみます。UILabelの幅が縮まることで隣合う要素はそのまま詰まります。上記の例だと、「4.5 ★★★★☆」をHiddenにしたときにUILabelの幅をゼロにすることで「Googleのクチコミ」を左に詰めます。

    まずは制約を2つつけましょう。横幅が「0」と「105」です。同じ制約で値が違うので、エラーがでます。そこで「Priority」を設定しましょう。横幅「0」の方をPriority「250」に下げてみました。すると、制約エラーは消えるはずです。

    制約を動的に消したりつけたりするために、プロパティとして作成しておきます。「strong」にしておきます。

    @property (strong, nonatomic) IBOutlet NSLayoutConstraint *ratingWidthNormalConstraint;
    @property (strong, nonatomic) IBOutlet NSLayoutConstraint *ratingWidthZeroConstraint;
    

    あとは、場合に応じて不要な制約を削除すればOK。以下のようになります。

        if (self.ratingLabel.hidden) {
            [self.ratingLabel removeConstraint:self.ratingWidthNormalConstraint];
        }else{
            [self.ratingLabel removeConstraint:self.ratingWidthZeroConstraint];
        }
    

    2015/09/01追記:
    制約のつけかえでもできますが、self.ratingWidthNormalConstraintの制約の値を0に変更してもOKです。
    そのほうが簡単ですね。

    AutoLayoutを設定していると、frameをコード上で変化させても反映されないので、上記のように制約をコントロールすることで対応する必要があります。「translatesAutoresizingMaskIntoConstraints」を使ってAutoLayoutを無効にする手もありますが、なるべくAutoLayout上で完結したほうがよいかと思います。

さいごに

AutoLayoutは慣れるととても便利ですよ。