1. ホーム
  2. swift

[解決済み] SwiftでnibからUIViewを読み込む

2022-04-23 12:34:47

質問

以下は、カスタマイズされたnibを読み込むために使用しているObjective-Cのコードです。 UIView :

-(id)init{

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
    return [subviewArray objectAtIndex:0];

}

Swiftで同等のコードとは?

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

オリジナルソリューション

  1. XIB と SomeView という名前のクラスを作成しました(同じ名前を使用するため)。 利便性と可読性)。両方ともUIViewをベースにしています。
  2. XIBで、"File's Owner"クラスをSomeViewに変更しました(アイデンティティインスペクタで)。
  3. SomeView.swift で UIView アウトレットを作成し、XIB ファイルのトップレベルのビューにリンクしました(便宜上 "view" と名付けました)。その後、必要に応じてXIBファイル内の他のコントロールに他のアウトレットを追加しました。
  4. SomeView.swiftの中で、"init with code"のイニシャライザーの中でXIBをロードしています。self"に何かを代入する必要はありません。XIBがロードされるとすぐに、トップレベルビューを含むすべてのアウトレットが接続されます。唯一欠けているのは、ビュー階層にトップビューを追加することです。

.

class SomeView: UIView {
   required init(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
      self.addSubview(self.view);    // adding the top level view to the view hierarchy
   }
   ...
}

この方法では、nibから自分自身をロードするクラスが得られることに注意してください。そうすれば、プロジェクトでUIViewが使われるときはいつでも(インターフェースビルダーやプログラムで)、SomeViewをクラスとして使うことができます。

更新 - Swift 3 の構文を使用

以下の拡張機能のxibの読み込みは、インスタンスメソッドとして記述し、上記のようなイニシャライザで使用することができます。

extension UIView {

    @discardableResult   // 1
    func fromNib<T : UIView>() -> T? {   // 2
        guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {    // 3
            // xib not loaded, or its top view is of the wrong type
            return nil
        }
        self.addSubview(contentView)     // 4
        contentView.translatesAutoresizingMaskIntoConstraints = false   // 5 
        contentView.layoutAttachAll(to: self)   // 6 
        return contentView   // 7
    }
}

  1. すべてのアウトレットがすでに接続されている場合、返されたビューは呼び出し元にとってほとんど興味がないため、廃棄可能な戻り値を使用します。
  2. このメソッドは、オプションの UIView 型のオブジェクトを返す汎用メソッドです。ビューの読み込みに失敗した場合は、nil を返します。
  3. 現在のクラスインスタンスと同じ名前の XIB ファイルをロードしようとします。これに失敗した場合、nil が返されます。
  4. ビュー階層にトップレベルのビューを追加する。
  5. この行は、ビューをレイアウトするために制約を使用していることを想定しています。
  6. このメソッドは、top, bottom, leading & trailing constraints - attaching view to "self" on all sides (参照)を追加します。 https://stackoverflow.com/a/46279424/2274829 詳細はこちら)
  7. トップレベルのビューを返す

そして、呼び出し側のメソッドは次のようなものになります。

final class SomeView: UIView {   // 1.
   required init?(coder aDecoder: NSCoder) {   // 2 - storyboard initializer
      super.init(coder: aDecoder)
      fromNib()   // 5.
   }
   init() {   // 3 - programmatic initializer
      super.init(frame: CGRect.zero)  // 4.
      fromNib()  // 6.
   }
   // other methods ...
}

  1. SomeClass は、SomeClass.xib ファイルからそのコンテンツをロードする UIView のサブクラスです。final" キーワードはオプションです。
  2. ビューがストーリーボードで使用されるときのためのイニシャライザーです(ストーリーボードビューのカスタムクラスとしてSomeClassを使用することを忘れないでください)。
  3. プログラムによりビューを作成する場合のイニシャライザー (例: "let myView = SomeView()").
  4. このビューはオートレイアウトでレイアウトされているため、オールゼロフレームを使用しています。 init(frame: CGRect) {...}"メソッドは独立して作成されないことに注意してください。
  5. & 6.拡張子を使ってxibファイルを読み込む。

クレジット:このソリューションで一般的な拡張子を使用することは、以下のRobertの回答からヒントを得ました。

編集 混乱を避けるため、"view" を "contentView" に変更しました。また、配列の添え字を".first"に変更。