LoginSignup
151

More than 5 years have passed since last update.

UIViewをNibから初期化する

Last updated at Posted at 2014-03-17

再利用可能なカスタムビュークラスを、Nib(xibファイル)を使って作る方法を考えます。例としてViewの上にLabelとButtonがひとつずつ乗っているカスタムビューを作ります。

サンプルコードがこちらにあります。

スクリーンショット 2014-03-18 0.31.28.png

ルートのViewにカスタムクラスを指定する方法

  • CustomView1.h, CustomView1.m, CustomView1.xibを作る
  • Interface Builderで図のようにView, Label, Buttonを配置する
  • ルートのViewのClassをCustomView1にする
  • File's Ownerはなし
  • LabelのoutletとButtonのactionをソースコード側とつなぐ

ソースコードは以下のように実装します。

// CustomView1.h

@interface CustomView1 : UIView
@property (weak, nonatomic) IBOutlet UILabel *label;
+ (instancetype)view;
@end
// CustomView1.m

#import "CustomView1.h"

@implementation CustomView1

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

    }
    return self;
}

- (void)awakeFromNib
{
    [super awakeFromNib];

    // 初期化
    self.label.text = NSStringFromClass([self class]);
}

+ (instancetype)view
{
    NSString *className = NSStringFromClass([self class]);
    return [[[NSBundle mainBundle] loadNibNamed:className owner:nil options:0] firstObject];
}

- (IBAction)buttonTapped:(id)sender
{
    NSLog(@"tapped.");
}

使う側のクラスでは以下のようにします。

CustomView1 *cv1 = [CustomView1 view];
[self.view addSubview:cv1];

-[NSBundle loadNibNamed:owner:options]を呼ぶとinitWithCoder:awakeFromNibが呼ばれます。初期化の処理はoutlet等がインスタンス化された後に呼ばれるawakeFromNibの中でやるのが良いでしょう。

[[CustomView1 alloc] init]のように使いたい場合は指定イニシャライザのinitWithFrame:をオーバーライドする必要があります。

- (id)initWithFrame:(CGRect)frame
{
    self = [[self class] view];
    return self;
}

この方法はNibから直接欲しいカスタムビュークラスをインスタンス化していてわかりやすいですが、カスタムビューを使う側はコードで書かなければならず、他のNib内で使う事ができません。

File's Ownerにカスタムクラスを指定する方法

  • CustomView2.h, CustomView2.m, CustomView2.xibを作る
  • CustomView1と同様にInterface BuilderでView, Label, Buttonを配置する
  • File's OwnerにCustomView2を指定する
  • IB上のViewはCustomView2のcontentViewというoutletにつなぐ
  • Label, ButtonはCustomView1と同様につなぐ

ソースコードは以下のように実装します。

// CustomView2.h

@interface CustomView2 : UIView
@property (weak, nonatomic) IBOutlet UIView *contentView;
@property (weak, nonatomic) IBOutlet UILabel *label;
@end
// CustomView2.m

#import "CustomView2.h"

@implementation CustomView2

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

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

- (void)p_commonInit
{
    NSString *className = NSStringFromClass([self class]);
    [[NSBundle mainBundle] loadNibNamed:className owner:self options:0];

    self.contentView.frame = self.bounds;
    [self addSubview:self.contentView];

    self.label.text = className;
}

- (IBAction)buttonTapped:(id)sender
{
    NSLog(@"tapped.");
}

Nibからインスタンス化したViewをselfにaddSubviewしているので、よけいなviewの階層がひとつ増えてしまいます。

しかし使う側は[[CustomView alloc] init]のようにコードから使う事もできますし、Interface Builderで配置したViewのクラスにCustomeView2を指定して利用する事もできます。前者の場合initWithFrame:が、後者の場合initWithCoder:が呼ばれるので、必要な処理は共通の初期化メソッドの中に書きます。

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
151