Custom Viewをxibファイルで作りたい時があると思います。そして、それをオブジェクト化する方法はいくつかあります。

Custom Viewクラスの初期化処理で、xibのViewをオブジェクト化して、それをaddSubviewする

自分はこれが最適解かなと思っています。理由は、

  • storyboard、xibでも、そのCustom Viewを使えるようになること
  • initWithFrameなどプログラムから生成したい場合も対応可能

になることです。

手短に手順を説明すると以下のようになります。

  • xibファイルを作る
  • xib上のViewにSubviewを配置する
  • Custom Viewとなるクラスを作る
  • xibのFile’s OwnerのCustom ClassをCustom Viewのクラス名にする
  • CustomViewのinitWithCoder、initWithFrameで、xibからViewをロードして、addSubviewで追加する

詳しくは以下の記事で説明しているので、そちらを参照してください。

Auto LayoutでCustom Viewを作る

ちなみにTableViewCellは、xibから直接オブジェクト化し、かつ、それがCustom Cellクラスのオブジェクトとなるようにできます。以下の記事を参照してください。

Auto LayoutでCustom TableViewCellを作る

xibのViewを直接オブジェクト化して、かつそれがCustomViewクラスのオブジェクトとなるようにする

さきほどの方法が若干不快なのは、Viewの上にViewが乗るため1View分余計にメモリを浪費するように思えることです。xibからロードしたらそれがそのままCustom Viewクラスのオブジェクトになる方がスマートに思えます。それがこの方法になります。

手順を手短に説明すると以下のようになります。

  • xibファイルを作る
  • xib上のViewにSubviewを配置する
  • Custom Viewとなるクラスを作る
  • xibのViewのCustom ClassをCustom Viewのクラス名にする(File’s Ownerの方ではないことに注意)
  • 下記の方法でロードする
// xibファイルの名前、クラス名が共にSTCustomViewの場合
UINib *nib = [UINib nibWithNibName:@"STCustomView" bundle:nil];
STCustomView *customView = [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];

わかりにくい場合は、この方法の詳細は以下の記事で説明しているので、そちらを参照してください。

ViewのxibをロードしてカスタムViewとひもづける

この方法は一見スマートに思えますが、先ほどの

  • storyboard、xibでも、そのCustom Viewを使えるようになること

の条件が満たせなくなります。

storyboard、xib上のViewにViewを配置し、そのCustom Classの項目をCustom Viewのクラス名にしても、xibはロードされません。単純にCustom Viewクラス単体でオブジェクト化されるだけで、まっさらのViewとしてロードされるだけです。Auto Layoutを使うようになると、View配置のほとんどはInterface Builderで行うことになるので、これでは困ります。

検索したところトリッキーな方法で、xibから直接オブジェクト化して、それがCustom Viewクラスのオブジェクトとなる方法を見つけました。

// storyboard、xibからロードされた場合に呼ばれる
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {
    if ([[self subviews] count] == 0) {
        UINib *nib = [UINib nibWithNibName:@"STCustomView" bundle:nil];
        customView *customView = [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];

        // frameを合わせる
        customView.frame = self.frame;

        // selfをxibからロードしたものにすり替える
        self = customView;
    }
    return self;
}

一見うまくいっているように見えたのですが、Auto Layoutと相性が悪くレイアウトがうまくいきませんでした。customView.frame = self.frameの後にconstraintsなども同等になるようにするなど対処すれば、うまくいくかもしれませんが、いずれにせよ、トリッキーなのでやめておいた方が良さそうです。

まとめ

一番最初に書いた方法が安全確実で良いと思います。1View分余計にメモリ浪費しますが、気にしないことにしました。たとえばTableViewCellもSubviewとしてcontentView、textLabel、detailLabelが含まれていますが、使わない場合もあります。これも無駄なメモリ浪費といえます。TableView側でCellを使いまわす仕組みがありますが、1画面に10Cellくらい表示されることはあるでしょう。

それと比べたら、Custom Viewをいくつか配置し、それが余計なViewも含むとしても気にするレベルではないはずです。他のアプリを圧迫するほど合計のメモリ浪費は大きくならないないでしょう。それよりも確実性とstoryboard、xibでCustom Viewが使える利便性のメリットを取ったほうが良いと思います。