1. ホーム
  2. ios

[解決済み] xibで再利用可能なUIViewを作成する(storyboardから読み込むことも可能)。

2023-06-20 13:41:19

質問

StackOverflow にこの件に関する投稿が何十件もありますが、解決策について特に明確なものはありません。私は、カスタム UIView を作成したいのですが、付属の xib ファイルが必要です。要件は次のとおりです。

  • 個別の UIViewController - 完全に自己完結したクラス
  • ビューのプロパティを設定/取得できるようにするためのクラス内のアウトレット。

これを行うための私の現在のアプローチは

  1. オーバーライド -(id)initWithFrame:

    -(id)initWithFrame:(CGRect)frame {
        self = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])
                                              owner:self
                                            options:nil] objectAtIndex:0];
        self.frame = frame;
        return self;
    }
    
    
  2. を使用してプログラム的にインスタンス化します。 -(id)initWithFrame: でインスタンス化します。

    MyCustomView *myCustomView = [[MyCustomView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
    [self.view insertSubview:myCustomView atIndex:0];
    
    

これは問題なく動作します(ただし、決して [super init] を呼び出さず、ロードされた nib の内容を使って単にオブジェクトを設定するのは、少し疑わしいと思われます。 でサブビューを追加するアドバイスがあります。 というアドバイスがありますが、これも問題なく動作します)。しかし、私はストーリーボードからビューをインスタンス化することができるようにしたいと思います。だから私はできる。

  1. を配置する。 UIView をストーリーボード内の親ビューに配置します。
  2. そのカスタムクラスを MyCustomView
  3. オーバーライド -(id)initWithCoder: - のようなコードで、私が最もよく目にするのは、以下のようなパターンに当てはまります。

    -(id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [self initializeSubviews];
        }
        return self;
    }
    
    -(id)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            [self initializeSubviews];
        }
        return self;
    }
    
    -(void)initializeSubviews {
        typeof(view) view = [[[NSBundle mainBundle]
                             loadNibNamed:NSStringFromClass([self class])
                                    owner:self
                                  options:nil] objectAtIndex:0];
        [self addSubview:view];
    }
    
    

もちろん、これはうまくいきません。上記の方法を使おうが、プログラムでインスタンス化しようが、どちらも再帰的に -(id)initWithCoder: に入った後 -(void)initializeSubviews と入力し、ファイルからnibを読み込む。

他のいくつかのSOの質問は、このようなことを扱っています。 ここで , ここ , ここ そして はこちら . しかし、与えられた答えのどれも満足のいく形で問題を解決することはできません。

  • 一般的な提案は、クラス全体を UIViewController に埋め込み、そこで nib の読み込みを行うことのようですが、これはラッパーとして別のファイルを追加する必要があるため、私には最適とは思えません。

どなたか、この問題を解決する方法、およびカスタム UIView でアウトレットを動作させる方法を教えてください。あるいは、最小限の定型的なコードで物事を行う、よりクリーンな代替方法があるのでしょうか?

どのように解決するのですか?

あなたの問題は loadNibNamed: の子孫)から initWithCoder: . loadNibNamed: は内部で initWithCoder: . もし、ストーリーボードコーダーをオーバーライドして、常にxibの実装をロードしたいのであれば、次のテクニックをお勧めします。ビュークラスにプロパティを追加し、xibファイルの中で、(User Defined Runtime Attributesに)あらかじめ決められた値を設定するのです。ここで [super initWithCoder:aDecoder]; を呼び出したら、プロパティの値を確認します。あらかじめ決められた値であれば [self initializeSubviews]; .

ということで、こんな感じ。

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

    if (self && self._xibProperty != 666)
    {
        //We are in the storyboard code path. Initialize from the xib.
        self = [self initializeSubviews];

        //Here, you can load properties that you wish to expose to the user to set in a storyboard; e.g.:
        //self.backgroundColor = [aDecoder decodeObjectOfClass:[UIColor class] forKey:@"backgroundColor"];
    }

    return self;
}

-(instancetype)initializeSubviews {
    id view =   [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];

    return view;
}