SwiftでnibからUIViewをロードする


148

これが私のカスタマイズしたnibを読み込むために使用しているObjective-CコードですUIView

-(id)init{

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

}

Swiftの同等のコードは何ですか?

回答:


172

元のソリューション

  1. XIBとSomeViewという名前のクラスを作成しました(便宜と読みやすさのために同じ名前を使用しました)。どちらもUIViewに基づいています。
  2. XIBでは、「ファイルの所有者」クラスを(IDインスペクターで)SomeViewに変更しました。
  3. SomeView.swiftでUIViewアウトレットを作成し、XIBファイルのトップレベルビュー(便宜上「ビュー」という名前が付けられたビュー)にリンクを作成しました。次に、必要に応じて、XIBファイルの他のコントロールに他のアウトレットを追加しました。
  4. SomeView.swiftで、「コードによる初期化」イニシャライザ内にXIBをロードしました。「自己」に何かを割り当てる必要はありません。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. このメソッドは、上、下、先頭、および末尾の制約を追加します-すべての側面で「自己」にビューをアタッチします(詳細は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サブクラスです。「最終」キーワードはオプションです。
  2. ビューがストーリーボードで使用されるときの初期化子(ストーリーボードビューのカスタムクラスとしてSomeClassを使用することを忘れないでください)。
  3. ビューがプログラムで作成されたときの初期化子(つまり、「let myView = SomeView()」)。
  4. このビューは自動レイアウトを使用してレイアウトされるため、すべてゼロのフレームを使用します。自動レイアウトはプロジェクトでのみ使用されるため、「init(frame:CGRect){..}」メソッドは独立して作成されないことに注意してください。
  5. &6.拡張子を使用してxibファイルをロードします。

クレジット:このソリューションで一般的な拡張機能を使用することは、以下のRobertの回答に触発されました。

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


7
クラス名を設定しFile's Ownerてスポットを当てます...ありがとうございます!
Aviel Gross、2015

13
UIViewにはプロパティビューがないため、self.viewを呼び出すとエラーが発生します
Nastya Gorban

4
この場合、@ NastyaGorban self.viewは、実際には、GK100が.xibのトップレベルビューからSomeView.swiftにリンクしたアウトレットプロパティ(「ビュー」という名前)を指します。そのアウトレットを追加しないと、「ビュー」がないためエラーが発生します。 。あなたが言うようNSViewのクラスの「プロパティ
Ausiàs

3
nib(loadNibNamed)をロードするとクラッシュします。Xcode 6.3とSwiftの使用
karthikPrabhu Alagu

11
呼び出しfromNib()内からinit(coder aDecoder: NSCoder)内部ペン先をロードするよう無限ループ作成fromNib():メソッドは呼び出しを可能にするinit(coder aDecoder: NSCoder)
マシューCawley

334

私の貢献:

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

次に、次のように呼び出します。

let myCustomView: CustomView = UIView.fromNib()

..あるいは:

let myCustomView: CustomView = .fromNib()

20
最善の答えです。
CodyMace 2017年

7
ここでベストアンサー。クリーンでシンプル
Marquavious Draggon 2017年

3
@YuchenZhong-オプションを返すため、.firstよりも[0]を選択します。強制的にアンラップすると、安全ではなくなります。...そしてこれは疑問を投げかけます:上記のソリューションの一部としてオプションを返さないのはなぜですか?回答:できます。何も問題ありません。しかし...それがnilを返す場合、xib /クラスの名前は一致しません。これは開発者のミスであり、すぐにキャッチし、本番環境に到達させないでください。ここでは、アプリを異常な状態のままにするよりも、アプリをクラッシュさせたいと思います。ちょうど私の2セント/好み。
Robert Gummesson 2017年

1
@allenlinli-このメソッドは、CustomViewに想定されるUIViewの静的な拡張です。これは、コンパイラが明示的な型注釈を使用して型を推論するため機能します。CustomViewはUIViewのサブクラスであり、型は既に推論されているため、再度推論する必要はありません。したがって、2番目の例に示すように、UIViewを省略できます。そうは言っても、あなたがそれをあなたがそれを置く方法と同じように呼んでいることを明らかにすることができました。
Robert Gummesson 2017年

3
.xib内にカスタムビューがあった場合、このソリューションは私にはうまくいきませんでした。この部分を次のように修正することをお勧めします:return Bundle.main.loadNibNamed(String(describing:self)、owner:nil、options:nil)![0] as!T
ナゼヤ

79

-> Self迅速に戻ることができるようになったことで、これが少し簡単になります。Swift 5で最後に確認されました。

extension UIView {
    class func fromNib(named: String? = nil) -> Self {
        let name = named ?? "\(Self.self)"
        guard
            let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
            else { fatalError("missing expected nib named: \(name)") }
        guard
            /// we're using `first` here because compact map chokes compiler on
            /// optimized release, so you can't use two views in one nib if you wanted to
            /// and are now looking at this
            let view = nib.first as? Self
            else { fatalError("view of type \(Self.self) not found in \(nib)") }
        return view
    }
}

あなたの場合は.xib、ファイルおよびサブクラスが同じ名前を共有して、あなたが使用することができます。

let view = CustomView.fromNib()

カスタム名がある場合は、次を使用します。

let view = CustomView.fromNib(named: "special-case")

注意:

「タイプYourTypeのビューが見つかりません。」というエラーが発生する場合は、.xibファイルにビューのクラスが設定されていません。

.xibファイルでビューを選択し、を押しcmd + opt + 4て、class入力でクラスを入力します


1
これをXCode 7.1ベータ3で動作させることはできません-ベータ版かどうかはわかりませんが、基本的にはSwiftのnibから直接カスタムビューを作成するためにあらゆる方法を試しましたが、常に同じ結果が得られます:作成しているクラスアウトレットに対応したKVCではありません。それが私が間違っていることかどうかはわかりませんが、私のクラスはかなり単純で、ファイルの所有者は正しいです。私はこれをObjective-Cの下でずっとしていました。
エシェロン2015年

1
@Loganそれはあなたのコードに実際には関係ありませんが、imoカスタムビューはStoryboard / XIBからのロードをサポートするべきです。私のコメントは、そのようなビューを作成したい人への通知にすぎませんでした
Nikita Took

1
この関数を呼び出す2番目の形式、つまりを使用しても問題が発生することに注意してくださいlet myCustomView = UIView.fromNib() as? CustomView。この場合、ではなくにT.self解決され、ペン先を見つけることができません。これがなぜなのかわかりません-多分関数が呼び出されたという意味の推測された型?UIViewCustomViewletUIView
エシェロン2015

2
ファイルの所有者を使用してコンセントを接続しようとすると(古き良き時代に行ったように)、これがクラッシュすることを指摘することが重要です。IBでは、ファイルの所有者はnil / emptyである必要があり、代わりにアウトレットがビューに接続されている必要があります。
Echelon

1
@Echelonあなたは私の日を救った!!! ファイルの所有者を使用してコンセントを接続しましたが、機能しませんでした。代わりにビューを使用しました。
Jan Schlorf、2018年

19

次のコードを試してください。

var uiview :UIView?

self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView

編集:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

    init()  {
        super.init()
       self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
    }


}

このメソッドは、Swiftのinit()であるオブジェクト初期化メソッド内で呼び出す必要があります。
Bagusflyer 14

12

Swift 4プロトコル拡張

public protocol NibInstantiatable {

    static func nibName() -> String

}

extension NibInstantiatable {

    static func nibName() -> String {
        return String(describing: self)
    }

}

extension NibInstantiatable where Self: UIView {

    static func fromNib() -> Self {

        let bundle = Bundle(for: self)
        let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)

        return nib!.first as! Self

    }

}

可決

class MyView: UIView, NibInstantiatable {

}

この実装は、NibがUIViewクラスと同じ名前を持つことを前提としています。例 MyView.xib。MyViewにnibName()を実装してこの動作を変更し、デフォルトのプロトコル拡張実装とは異なる名前を返すことができます。

xibでは、ファイルの所有者はMyViewであり、ルートビュークラスはMyViewです。

使用法

let view = MyView.fromNib()

3
これはこれまでで最もエレガントで簡単な解決策であり、なぜそれが受け入れられない答えではないのか私にはわかりません!
horseshoe7

@ horseshoe7は、質問から4年後に作成されたためです。
Freeubi

11

私は次のコードによってSwiftでこれを達成しました:

class Dialog: UIView {
    @IBOutlet var view:UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.frame = UIScreen.mainScreen().bounds
        NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
        self.view.frame = UIScreen.mainScreen().bounds
        self.addSubview(self.view)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

あなたのXIBの接続することを忘れないでくださいビューにコンセントをビュー迅速に定義されているコンセント。First Responderをカスタムクラス名に設定して、追加のコンセントの接続を開始することもできます。

お役に立てれば!


10

Xcode 7ベータ4、Swift 2.0、iOS9 SDKでテスト済み。次のコードは、xibをuiviewに割り当てます。このカスタムxibビューをストーリーボードで使用して、IBOutletオブジェクトにアクセスすることもできます。

import UIKit

@IBDesignable class SimpleCustomView:UIView
{
    var view:UIView!;

    @IBOutlet weak var lblTitle: UILabel!

   @IBInspectable var lblTitleText : String?
        {
        get{
            return lblTitle.text;
        }
        set(lblTitleText)
        {
            lblTitle.text = lblTitleText!;
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        loadViewFromNib ()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib ()
    }
    func loadViewFromNib() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        self.addSubview(view);



    }


}

プログラムでcustomviewにアクセスする

self.customView =  SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
        self.view.addSubview(self.customView!);

ソースコード-https://github.com/karthikprabhuA/CustomXIBSwift


9

プロジェクトに多くのカスタムビューがある場合は、次のようなクラスを作成できます。 UIViewFromNib

Swift 2.3

class UIViewFromNib: UIView {

    var contentView: UIView!

    var nibName: String {
        return String(self.dynamicType)
    }

    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)

        loadViewFromNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        loadViewFromNib()
    }

    //MARK:
    private func loadViewFromNib() {
        contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
        contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

スウィフト3

class UIViewFromNib: UIView {

    var contentView: UIView!

    var nibName: String {
        return String(describing: type(of: self))
    }

    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)

        loadViewFromNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        loadViewFromNib()
    }

    //MARK:
    func loadViewFromNib() {
        contentView = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?[0] as! UIView
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

また、すべてのクラスでから継承するだけで、ファイルの名前が異なる場合はプロパティUIViewFromNibをオーバーライドできます。nibName.xib

class MyCustomClass: UIViewFromNib {

}

8

上記のソリューションに基づいて構築します。

これはすべてのプロジェクトバンドルで機能し、fromNib()を呼び出すときにジェネリックは必要ありません。

スウィフト2

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nil)
    }

    public class func fromNib(nibName: String?) -> Self {

        func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
            let bundle = NSBundle(forClass: T.self)
            let name = nibName ?? String(T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName)
    }
}

スウィフト3

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nibName: nil)
    }

    public class func fromNib(nibName: String?) -> Self {
        func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
            let bundle = Bundle(for: T.self)
            let name = nibName ?? String(describing: T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName: nibName)
    }
}

このように使用できます:

let someView = SomeView.fromNib()

またはこのように:

let someView = SomeView.fromNib("SomeOtherNibFileName")

6

スウィフト4

".first as?CustomView"と書くことを忘れないでください。

if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {    
    self.view.addSubview(customView)
    }

どこでも使いたいなら

最良の解決策は、Robert Gummessonの答えです。

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

次に、次のように呼び出します。

let myCustomView: CustomView = UIView.fromNib()

5
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]

しかし、Swiftのinit()では、戻り値はありません。UIViewの初期化でloadNibNamedを呼び出す必要があることを忘れていました。
Bagusflyer 14

「戻り値なし」とはどういう意味ですか?selfはすべてのinitメソッドから暗黙的に返されます...
Grimxn 2014

1
つまり、initメソッド内でloadNibNamedを呼び出します。ロードされたUIViewは、ObjCで自分自身に割り当てられます。しかし、迅速に言えば、そうではありません。
Bagusflyer 2014

5

Swiftでこれを行う良い方法は、列挙型を使用することです。

enum Views: String {
    case view1 = "View1" // Change View1 to be the name of your nib
    case view2 = "View2" // Change View2 to be the name of another nib

    func getView() -> UIView? {
        return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil).first as? UIView
    }
}

次に、コードで簡単に使用できます:

let view = Views.view1.getView()

2
あなたは空のnibファイルまたはnoneのUIViewのルートノードとのnibファイルでこれを行う場合は、配列のサイズや位置0の要素をチェックする正気でないと、あなたがクラッシュすることに注意してください
マシューCawley

4

私はこのソリューションを好んでいます(@ GK100の場合の回答に基づく):

  1. XIBとSomeViewという名前のクラスを作成しました(便宜と読みやすさのために同じ名前を使用しました)。どちらもUIViewに基づいています。
  2. XIBでは、「ファイルの所有者」クラスを(IDインスペクターで)SomeViewに変更しました。
  3. SomeView.swiftでUIViewアウトレットを作成し、XIBファイルのトップレベルビュー(便宜上「ビュー」という名前が付けられたビュー)にリンクを作成しました。次に、必要に応じて、XIBファイルの他のコントロールに他のアウトレットを追加しました。
  4. SomeView.swiftでは、initまたはinit:frame: CGRect初期化子内にXIBをロードしました。「自己」に何かを割り当てる必要はありません。XIBがロードされるとすぐに、最上位のビューを含むすべてのアウトレットが接続されます。唯一欠けているのは、トップビューをビュー階層に追加することです。

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

私はフレームでinitを使うのが好きなので、これを根こそぎにしました!もう一つ注意すべき...あなたはあなたが合格枠と一致するビューたい場合self.view.frame =フレームを追加
マイク・E

3

ローガンの回答のSwift 3バージョン

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
            for nibView in nibViews {
                if let tog = nibView as? T {
                    view = tog
                }
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nib: UINib? {
        if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: nil)
        } else {
            return nil
        }
    }
}

3

以下は、プロトコルとプロトコル拡張(Swift 4.2)を使用してプログラムでビューをロードするクリーンで宣言的な方法です。

protocol XibLoadable {
    associatedtype CustomViewType
    static func loadFromXib() -> CustomViewType
}

extension XibLoadable where Self: UIView {
    static func loadFromXib() -> Self {
        let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
        guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
            // your app should crash if the xib doesn't exist
            preconditionFailure("Couldn't load xib for view: \(self)")
        }
        return customView
    }
}

そしてあなたはこれを次のように使うことができます:

// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }

// and when you want to use it
let viewInstance = MyView.loadFromXib()

その他の考慮事項

  1. カスタムビューのxibファイルにCustom Class、ファイルの所有者ではなく、ビューのセット(およびそこからのアウトレット/アクションセット)があることを確認します。
  2. このプロトコル/拡張機能は、カスタムビューの外部または内部で使用できます。ビューを初期化するときに他の設定作業がある場合は、内部で使用することをお勧めします。
  3. カスタムビュークラスとxibファイルは同じ名前にする必要があります。

2

私はこのようにしています:

if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
// Do something with myView
}

このサンプルでは、​​メインバンドルのnib "MyView.xib"の最初のビューを使用します。ただし、インデックス、nib名、またはバンドル(デフォルトではmain)を変更できます。

私は以前、ビューのinitメソッドにビューを呼び起こしたり、上記のソリューションのようにジェネリックメソッドを作成したりしていました(ちなみにスマートです)が、もうそれはしません。

このようにして、同じビューロジックとコードを維持しながら、さまざまなレイアウトや特性を使用できます。

必要に応じて、ファクトリオブジェクト(通常はビューを使用するviewController)に作成させる方が簡単です。所有者が必要な場合があります(通常、作成されたビューが作成者に接続されたアウトレットを取得した場合)。

それがおそらく、AppleがinitFromNibUIViewクラスにメソッドを含めなかった理由です...

地上レベルの例をとると、あなたは自分がどのように生まれたかがわかりません。あなたは生まれたばかりです。ビューもそうです;)


2

UIViewクラスでinitメソッドを呼び出すだけです。

そのようにしてください:

class className: UIView {

    @IBOutlet var view: UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
    }

    func setup() {
        UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
        addSubview(view)
        view.frame = self.bounds
    }
}

ここで、このビューをビューコントローラーのサブビューとして追加する場合は、ビューコントローラー.swiftファイルでそのようにします。

self.view.addSubview(className())

それは素晴らしい答えですが、何かが間違っています、私はそれを編集します。
C0mrade 2016年

それは私が実装した方法です。しかし、あなたはそれを即興にすることができます。C0mrade @事前のおかげで
ALAP Anerao

1

上記の回答のいくつかに似ていますが、より一貫したSwift3 UIView拡張機能:

extension UIView {
    class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
        let bundle = bundle ?? Bundle.main
        let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
        return nibViews?.first as? A
    }

    class func fromNib<T: UIView>() -> T? {
        return fromNib(nibName: String(describing: T.self), bundle: nil)
    }
}

これは、自分で名前を付けたnibからだけでなく、他のnib /バンドルからもクラスをロードできるという便利さを提供します。


1

ストーリーボードを介してこれを行うことができます。ビューに適切な制約を追加するだけです。あなた自身の例からビューをサブクラス化することでこれを簡単に行うことができますBaseView

Objective-C

BaseView.h


/*!
 @class BaseView
 @discussion Base View for getting view from xibFile
 @availability ios7 and later
 */
@interface BaseView : UIView

@end


BaseView.m


#import "BaseView.h"

@implementation BaseView

#pragma mark - Public

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - LifeCycle

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

#pragma mark - Private

- (void)prepareView
{
    NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
    UIView *view = [nibsArray firstObject];

    view.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:view];
    [self addConstraintsForView:view];
}

#pragma mark - Add constraints

- (void)addConstraintsForView:(UIView *)view
{
    [self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeBottom
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeBottom
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeTop
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeTop
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeLeft
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeLeft
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeRight
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeRight
                                                       multiplier:1.0
                                                         constant:0]
                           ]];
}

@end

スウィフト4

import UIKit

class BaseView : UIView {

    // MARK: - LifeCycle

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        prepareView()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        prepareView()
    }

    internal class func xibName() -> String {
        return String(describing: self)
    }

    // MARK: - Private
    fileprivate func prepareView() {
        let nameForXib = BaseView.xibName()
        let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
        if let view = nibs?.first as? UIView {
            view.backgroundColor = UIColor.clear
            view.translatesAutoresizingMaskIntoConstraints = false
            addSubviewWithConstraints(view, offset: false)
        }
    }
}

UIView+Subview


public extension UIView {
    // MARK: - UIView+Extensions

    public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
        subview.translatesAutoresizingMaskIntoConstraints = false
        let views = [
            "subview" : subview
        ]
        addSubview(subview)

        var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
        constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
        NSLayoutConstraint.activate(constraints)
    }
}

制約を追加する方法を2種類提供します-一般的なものとビジュアルフォーマット言語内-必要なものを選択します:)

また、デフォルトでは、xib名前は実装クラス名と同じ名前であると想定されていました。いいえの場合-変更するだけxibNameパラメータをするです。

ビューをサブクラス化する場合BaseView-簡単に任意のビューを配置し、IBでクラスを指定できます。


1
class func loadFromNib<T: UIView>() -> T {
    let nibName = String(describing: self)
    return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}

0

ローガンの答えに基づくより強力なバージョン

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
            if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
                view = tog
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nibIndex: Int {
        return 0
    }

    public class var nibBundle: Bundle {
        return Bundle.main
    }
}

そしてあなたはのように使うことができます

class BaseView: UIView {
    override class var nibName: String { return "BaseView" }
    weak var delegate: StandardStateViewDelegate?
}

class ChildView: BaseView {
    override class var nibIndex: Int { return 1 }
}

0

最も便利な実装。ここでは、UIViewではなくクラスのオブジェクトに直接戻るために、2つのメソッドが必要です。

  1. クラスとしてマークされたviewId 、オーバーライドを許可
  2. .xibには最上位レベルの複数のビューを含めることができます。この状況も正しく処理されます。

extension UIView {

class var viewId: String {
    return String(describing: self)
}

static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
                    owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {

    return instancePrivate(from: bundle ?? Bundle.main,
                           nibName: nibName ?? viewId,
                           owner: owner,
                           options: options)
}

private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
                                              owner: Any?, options: [AnyHashable : Any]?) -> T? {

    guard
        let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
        let view = views.first(where: { $0 is T }) as? T else { return nil }

    return view
}
}

例:

guard let customView = CustomView.instance() else { return }

//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true

0

Swift UIViewサブクラスを完全に自己完結させ、Nibを使用する実装の詳細を公開せずにinitまたはinit(frame :)を使用してインスタンス化する機能が必要な場合は、プロトコル拡張を使用してこれを実現できます。このソリューションは、他の多くのソリューションで提案されているように、ネストされたUIView階層を回避します。

public class CustomView: UIView {

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var valueLabel: UILabel!

    public convenience init() {
        self.init(frame: CGRect.zero)
    }

    public override convenience init(frame: CGRect) {
        self.init(internal: nil)
        self.frame = frame
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    fileprivate func commonInit() {
    }
}

fileprivate protocol _CustomView {
}

extension CustomView: _CustomView {
}

fileprivate extension _CustomView {

    // Protocol extension initializer - has the ability to assign to self, unlike
    // class initializers. Note that the name of this initializer can be anything
    // you like, here we've called it init(internal:)

    init(internal: Int?) {
        self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
    }
}

0
  let bundle = Bundle(for: type(of: self))
   let views = bundle.loadNibNamed("template", owner: self, options: nil)
    self.view.addSubview(views?[0] as! UIView)

コードのみの回答はお勧めしません。これにより問題がどのように解決されるか、またはこれが既存の回答とどのように異なるかについて説明を追加してください。口コミから
Nick

0

私は以下の拡張子を好む

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

この拡張機能と最もよく回答された拡張機能の違いは、定数や変数を保存する必要がないことです。

class TitleView: UIView { }

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

self.navigationItem.titleView = TitleView.instanceFromNib

どのバージョンのXcodeを使用していますか?XCodeの最新バージョンを使用していることを確認してください。XCode 11.5(最新の最新バージョン)でうまく動作します。
iCoder

0
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.