243
225

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AutoLayoutでViewのサイズやUIレイアウトをパーセント指定する方法

Last updated at Posted at 2014-12-02

2015/3/1更新:
「必見!」とかドヤァしておきながら、なんか凄い抜けたことしていたみたいで、
文中で説明しているダミーUIViewを使わなくても、普通に親のViewとEqualWidth/EqualHeightできたみたいです。失礼しました。指摘してくださった方、ありがとうございます。詳しくはコメント欄をご覧ください。
(コメントには書きましたが、親Viewのサイズがちょっとそのままでは使いたくない値だった場合は、ダミーUIView作っていいと思います)

iOS開発者・UIデザイナー必見!

iOSのStoryboardのAutoLayout。

UIコンポーネントの幅・高さはポイント数値指定しかできない。パーセント指定できないこともないけど、それにはコードでコンストレイントをいちいち記述しないといけない。

こんな風に↓
http://saveme-dot-txt.blogspot.jp/2014/03/percentage-based-layouts-using-mostly.html(SaveMe.txt様より)

そう思っていた時期がボクにもありました。

いや、でも実はできちゃうんですよ。Storyboard上だけで幅・高さのパーセント指定が。

そのコツをお伝えしましょう。

ダミーのUIViewとEqual Widths/Equal Heightsコンストレイントを使え!

Storyboard上で設定できるコンストレイントに、Equal WidthsとEqual Heightsがあることをご存知ですか?

これは何かというと、二つ以上のUI部品を選んだ状態でのみ設定できるコンストレイントで、選んだ複数のUI部品の幅または高さを、同じ値に揃えてくれるものです。

設定値の単位は、ポイントではなく、比率です。比率ですよ。大事なので2回言いました。つまり、パーセント!

このEqual WidthsコンストレイントまたはEqual Heightsコンストレイントを上手く使えばいいのです。

さて、あるクライアントから、かなり複雑な画面レイアウトを注文されたとしましょう。しかも、それはiPhone4SからiPhone6 Plusまで、全ての機種でレイアウトが乱れず一貫していること、という条件付き。

StoryBoardには、残念ながらJavaのSwingやJavaFXにあるような、高度なレイアウトクラスはありません。コンストレイントを駆使するしか無いのです。

ベースとなる考えは、画面をUIViewで区画分けすることです。

例えば以下の様な感じに。

percentlayout.jpg

さて、上記の図のようなUIViewによる区画分けはどうやればいいんでしょうか? ポイント固定でやったりしては、iPhone4SからiPhone6 Plusまでスケーラブルに対応できません。
ここで、Equal Widths/Equal Heightsコンストレイントの出番です。

ではまず、UIView「A」の領域を設定することにしましょう。しかし、Equal Widths/Equal Heightsコンストレイントを設定するには、同階層に存在する「もう片方」のUI要素が必要です。

もう片方? そう、ここで出てくるのが「ダミーUIView」(勝手に命名)です。

まず、「ダミーUIView」となるUIViewをStoryBoardの画面上に配置してください。そして、以下のようにコンストレイントを設定します。

スクリーンショット 2014-12-02 21.42.54.jpg

上記のコンストレイントを設定すると、高さが画面の縦サイズ(注:ステータスバー除く)と同じで、幅がゼロ、つまり面積ゼロの超細長いUIViewが、画面左端(別に右端でもOKです)にくっついた状態となります。
このUIViewの名前を、後で識別しやすいように分かりやすく「VerticalCriterionView」と設定しましょう。

その名の通り、このダミーUIViewは、ダミーでありながら、縦の長さの基準(Criterion)という重要な役割を担うのです。

さて、次にUIView「A」を配置して、画面上端にくっつけましょう。UIView「A」を選択して、以下のコンストレイトを設定します。ここでは、「Update Frames」は「None」(コンストレイントは設定するが、描画は更新しない)にしておきます。

スクリーンショット 2014-12-02 21.49.10.jpg

さぁそして、いよいよEqual Heightsコンストレイントの出番です。
「VerticalCriterionView」と、UIView「A」の両方を選択して、Equal Heightsコンストレイントを設定します。

スクリーンショット 2014-12-02 21.46.21.jpg

すると、UIView「A」が画面全体を覆ってしまいました(「Update Frames」を「Items of New Constraints」にしたため、描画の更新が行われた)。これは、Equal Heightsコンストレイントの設定値(Multiplier)のデフォルトが1であるためです。

つまり、ステータスバーを除いた画面の高さと同じ高さである「VerticalCriterionView」と、全く同じ高さがUIView「A」に設定されたわけです。だから、画面を覆い尽くしてしまったんですね。

スクリーンショット 2014-12-02 21.55.57.jpg

さて、ここであわてず、XCode左側のビュー階層ペインから、Equal Heightsコンストレイントを選択します。

すると、XCode右側のプロパティパネルで、Equal Heightsコンストレイントの設定値(Multiplier)を変更することができます。

スクリーンショット-2014-12-02-21.57.43.png

ここで、0.25を入力してみましょう。うまくいけば、綺麗にUIView「A」の高さが「VerticalCriterionView」(つまり、ステータスバーを除いた画面の高さ)の25%になるはずです。

もし、高さが縮むどころかもっと伸びてしまった場合は、下図のように、設定対象を入れ替えれば、ちゃんと25%になってくれます。

スクリーンショット 2014-12-02 22.03.05.jpg

さて、これで、まずUIView「A」の区画設定は完了ですね。

次にUIView「B」に移りましょう。もう、勘の良い読者なら、やり方はわかりますね。「VerticalCriterionView」とUIView「B」を選択して、Equal Heightsコンストレイントを設定、コンストレイント値を0.5に設定すればよいのです。

さて、今度はUIView「C」。これもAやBと同じようにしてもよいのですが、最後の1つは手を抜くことができます。単純に以下のコンストレイントを設定すれば良いのです。

スクリーンショット 2014-12-02 22.06.29.jpg

AとBの比率が決まっているなら、残りのCのサイズは自ずと決まりますからね。

入れ子にしよう&横バージョンに挑戦!

さて、今度はUIView「B」の中を、横に2分割しましょう。そのためには、UIView「B」の中に、新規UIViewを2つ配置することになります。それぞれ、UIView「B1」とUIView「B2」という名前だとしましょう。

勘の良い読者なら、横バージョンのやり方はもうわかりますね。

ダミーUIViewをまず最初に配置し(今度は横なので「HorizontalCriterionView」という名前にしましょう)、以下のようにコンストレイントを設定します。

スクリーンショット 2014-12-02 22.09.42.jpg

そして、UIView「B」を配置。以下のコンストレイントを設定した後、

スクリーンショット 2014-12-02 22.13.35.jpg

「HorizontalCriterionView」とUIView「B1」に対してEqual Widthsコンストレイントを設定。

スクリーンショット 2014-12-02 22.15.00.jpg

コンストレイント値(Multiplier)を0.4に設定します。

スクリーンショット 2014-12-02 22.17.13.jpg

UIView「B2」についても、同様にやってもいいんですが、また楽をしちゃいましょう。以下のようにコンストレイントを設定します。

スクリーンショット 2014-12-02 22.06.29.jpg

これで、パーセント指定レイアウトの出来上がり! iPhone4Sから6 Plusまで、各画面サイズでもスケーラブルにレイアウトが維持されていることを確認してみましょう。バッチリですね!

スクリーンショット 2014-12-02 22.19.30.jpg

UIViewだけでなく、他のUI部品にも有効

見出しですでに書いちゃってますが、このパーセント指定法はUIViewだけでなく、UILabelやUIButton、UIImageViewなど、ほとんどのUI部品に適用可能です。

フォントサイズはどうする?

さて、レイアウトはStoryboard上のみでパーセント指定できましたが、UILabelなどのフォントサイズは、各機種の画面サイズによってスケーラブルにする方法はないのでしょうか?

残念ながら、今のところはないようです。こればっかりは、コード上でフォントサイズを計算してスケールさせるしかありません。

私の場合は、StoryBoard上でUILabelなどのフォントサイズを、iPhone5の画面サイズを前提に設定しています。

それらのUILabelなどをコード側にアウトレット接続し、
-(void)viewWillAppear:(BOOL)animated
などの関数の中で、フォントサイズをスケールさせています。

具体的には、以下の様な感じです。

- (CGFloat)scaleFontSize
{
    if ([UIScreen mainScreen].bounds.size.Heights >= 700) { // 縦が700以上だったら、iPhone 6 Plus の標準解像度設定とみなす
        return 1.29375; //   (iPhone6 Plusの画面横幅)/(iPhone5の画面横幅)
    } else if ([UIScreen mainScreen].bounds.size.Heights >= 650){ // 縦が650以上だったら、iPhone 6 の標準解像度設定とみなす
        return 1.171875; //   (iPhone6の画面横幅)/(iPhone5の画面横幅)
    }
    
    return 1.0;
}

-(void)viewWillAppear:(BOOL)animated {
    CGFloat scale = [self scaleFontSize];

 self.titleLabel.font = [UIFont systemFontOfSize:25.0f * scale];

}

うーん。Storyboard上で同様のことができるような、もっと良い方法ないですかねぇ? ご存知の方いたら教えて下さい。

ちなみに、UIImageViewなどをコード上でスケールさせたい場合は、UIViewのtransformプロパティとCGAffineTransformMakeScale関数を使う方法がお勧めです。

さいごに

さて、いかがだったでしょうか。
上記の方法による、縦と横のパーセント指定と、UIViewの入れ子を駆使すれば、大抵のレイアウトは実現可能です。

これで、どんな複雑なUI画面でも、「全機種対応で!」とか言われても、怖くない(?)ですね!

243
225
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
243
225

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?