Auto LayoutでCustom Viewを作成し、それをViewController#viewに配置する方法について説明します。

サンプルコード: https://github.com/stack3/iOSAutoLayoutSamples

サンプルコードのCustomViewを選択すると以下の画面が表示されます。

001

横画面にするとこうなります。

002

STCustomView.xib

グレー背景の部分がCustomViewで、これはSTCustomView.xibでレイアウトされています。

003

構成は以下のとおり。

  • UIView – backgroundColorをgrayにしている
  • UIImageView – アイコン表示
  • UILabel – 文字列表示

UIImageViewのConstraintは以下のようになっています。

004

005

要するに幅と高さは32pxでSuperviewとの間隔は8pxということです。

ちなみに以下のようにWidthとHeightのConstraintがLeading Space to: Superviewと表示されることがあります・・・バグですね。

006

表示はバグっていても、右端の歯車アイコンを押すと、きちんと高さのConstraintへアクセスできます。

UILabelのConstraintは以下のようになっています。

007

016
Superviewの垂直方向中心に配置し、左のImageViewとの間隔は8px、右のSuperviewとの間隔は10pxです。

STCustomViewクラス

customViewCommonInitメソッドで初期化処理をしています。

- (void)customViewCommonInit
{
    // STCustomView.xibをロードし、Viewを得る
    UIView *view = [[[NSBundle mainBundle] loadNibNamed:@"STCustomView" owner:self options:nil] objectAtIndex:0];
    // ロードしたViewのframeをSuperviewのサイズと合わせる
    view.frame = self.bounds;
    // Superviewのサイズが変わった時に一緒に引き伸ばされれるように設定。
    // 以下は明示的に設定しなくてもデフォルトでそうなっているが念のため。
    // こういう場合は、Visual Format Languageを使うよりAutoresizingMaskを使ったほうが手軽。
    view.translatesAutoresizingMaskIntoConstraints = YES;
    view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
    // SuperviewにロードしたViewをSubviewとして追加
    [self addSubview:view];

    // 画像設定
    _imageView.image = [UIImage imageNamed:@"stack3"];
    // 文字列設定
    _titleLabel.text = @"Stack3";
}

customViewCommonInitはinitWithCoder、initWithFrameで呼ぶようにします。

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self customViewCommonInit];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self customViewCommonInit];
    }
    return self;
}

これでStoryboardやxibなどInterface Builderで配置された場合、initWithFrameでプログラムから生成された場合、両方に対応できます。

STCustomViewController.storyboard

Storyboardで以下のようにCustomViewを配置しています。

009

一旦Viewとして配置して、Identify InspectorからCustom ClassをSTCustomViewにします。

010

これでSTCustomViewとしてロードされます。

高さのConstraintは少し特殊な対応をしています。STCustomViewの高さは48pxなので、Storyboard上でも高さのConstraintを48pxにしておけばうまく表示されます。ただ仕様の変更などでCustomViewの高さが変わることはよくあります。その都度、高さのConstraintの値を変更するのは面倒です。

Auto LayoutでレイアウトされたCustomViewは自身の高さを自身で算出できます。今回はそれに頼ることにします。

CustomViewのConstraintは以下のようになっています。

012

レイアウトの方は高さが10pxだとわかりづらいので、ちょっと広げてみると以下のようになっています。

013

高さのConstraintが2つあり、片方は10px、もう片方は10px以上になっています。10pxの方は点線になっているのはPriorityが1に設定されているからです。つまり10以上の方がPriorityが高くなります。これによりCustomViewは自身で自動計算した適切な高さで配置されることになります。

10px以上の方だけあれば良いはずなのですが、なぜかAuto LayoutはConstraintが不十分だという警告を表示します。それを消すために10pxのConstraintも設定しています。ちょっと苦肉の策っぽいですが・・・ちなみにUILabelなどでも10px以上のConstraintだけでも警告は出ません。

CustomViewの適切な高さが10px未満、たとえば5pxとかだと上記ではうまくいきませんが・・・本当は高さ0pxと0px以上のConstraintを設定するのが確実です。ただそうしてしまうとInterface Builderのレイアウト上でCustomViewが視認しづらくなってしまう問題があります。今回は最低でも10pxはあるだろうから視認性も考慮して10px以上ということにしています。

念のため高さのConstraintを2つ配置する手順を説明しておきます。

  • CustomViewを選択して、Pinボタンを押す。
  • Heightにチェックして10pxとする。
  • CustomViewを選択して、もう一度Pinボタンを押す。
  • Heightにチェックして10pxとする。
  • 2つめは10px以上となるはず。

014

  • 10pxの方のConstraintのAttributes InspectorからPriorityを変更する。1にしなくても500でもうまくいきます。

015

つづく