SwiftでXIBファイルを使用してカスタムUIViewクラスを初期化/インスタンス化する方法


139

MyClassというサブクラスであるというクラスがありUIViewXIBファイルで初期化したいと思います。呼び出されたxibファイルでこのクラスを初期化する方法がわかりませんView.xib

class MyClass: UIView {

    // what should I do here? 
    //init(coder aDecoder: NSCoder) {} ?? 
}

5
iOS9スイフトのための完全なソースコードサンプル2.0参照 github.com/karthikprabhuA/CustomXIBSwift と関連するスレッドstackoverflow.com/questions/24857986/...
karthikPrabhu Alagu

回答:


266

私はこのコードをテストしましたが、うまくいきます:

class MyClass: UIView {        
    class func instanceFromNib() -> UIView {
        return UINib(nibName: "nib file name", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView
    }    
}

ビューを初期化し、以下のように使用します。

var view = MyClass.instanceFromNib()
self.view.addSubview(view)

または

var view = MyClass.instanceFromNib
self.view.addSubview(view())

UPDATE Swift> = 3.x&Swift> = 4.x

class func instanceFromNib() -> UIView {
    return UINib(nibName: "nib file name", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}

1
それはする必要がありますvar view = MyClass.instanceFromNib()self.view.addSubview(view)とは対照的に、var view = MyClass.instanceFromNibself.view.addSubview(view())。答えを改善するためのほんの小さな提案:)
ローガン

1
私の場合、ビューは後で初期化されました!self.view.addSubview(view)の場合、ビューはvar view = MyClass.instanceFromNib()
Ezimet

2
@Ezimetそのビュー内のIBActionsについてはどうですか。それらを処理する場所。私のビュー(xib)にボタンがあるように、そのボタンのIBActionクリックイベントを処理する方法。
カディールフセイン

6
UIViewだけでなく「MyClass」を返す必要がありますか?
Kesong Xie 2017

2
IBoutletsはこのアプローチでは機能しません...「このクラスはキーのコーディングに準拠したキー値ではありません」
Radek Wilczak

82

Samのソリューションはすでに優れていますが、さまざまなバンドルが考慮されておらず(NSBundle:forClassが役に立ちます)、コードを入力するために手動でロードする必要があります。

Xibアウトレット、さまざまなバンドル(フレームワークで使用!)を完全にサポートしてストーリーボードでプレビューを表示したい場合は、次を試してください。

// NibLoadingView.swift
import UIKit

/* Usage: 
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/

@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

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

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

    private func nibSetup() {
        backgroundColor = .clearColor()

        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView

        return nibView
    }

}

いつものようにxibを使用します。つまり、アウトレットをファイル所有者に接続し、ファイル所有者クラスを独自のクラスに設定します。

使用法:NibLoadingViewから独自のViewクラスをサブクラス化し、クラス名をXibファイルのFile's Ownerに設定します

追加のコードは必要ありません。

クレジット期限のあるクレジット:GHのDenHeadlessからの小さな変更でこれを分岐しました。私の要点:https : //gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8


8
このソリューションブレーキは接続を出口(それがサブビューとしてロードされたビューを追加するため)との呼び出しnibSetupからinit?(coder:)埋め込む場合、無限再帰が発生しますNibLoadingViewXIBに。
redent84 16

3
@ redent84コメントと反対票をいただきありがとうございます。2番目の外観がある場合は、以前のSubViewを置き換える必要があります(新しいインスタンス変数は指定されていません)。あなたは無限の再帰について正しいです、それはIBに苦労しているなら省略されるべきです。
フレデリクヴィンケルスドルフ2016

1
前述のように、「アウトレットをファイル所有者に接続し、ファイル所有者クラスを独自のクラスに設定します。」アウトレットをファイル所有者に接続する
Yusuf X

1
私はこの方法を使用してxibからビューをロードすることに常に不快です。基本的に、クラスAのサブクラスでもあるビューに、クラスAのサブクラスであるビューを追加します。この反復を防ぐ方法はありませんか?
Prajeet Shrestha 2017

1
@PrajeetShresthaこれは.clearColor()、ストーリーボードからロードした後に、nibSetup()が背景色をオーバーライドするためと考えられます。しかし、インスタンス化された後にコードで実行すれば機能するはずです。とにかく、前述したように、さらに洗練されたアプローチはプロトコルベースのアプローチです。ですから、ここにリンクがあります:github.com/AliSoftware/Reusable。私はUITableViewCells(本当に便利なプロジェクトを発見する前に実装しました)に関して同様のアプローチを使用しています。ああ!
フレデリクヴィンケルスドルフ2017年

77

Swift 2.0以降では、プロトコル拡張を追加できます。私の意見では、戻り値の型がSelfではなくUIView、呼び出し元がビュークラスにキャストする必要がないため、これはより良いアプローチです。

import UIKit

protocol UIViewLoading {}
extension UIView : UIViewLoading {}

extension UIViewLoading where Self : UIView {

  // note that this method returns an instance of type `Self`, rather than UIView
  static func loadFromNib() -> Self {
    let nibName = "\(self)".characters.split{$0 == "."}.map(String.init).last!
    let nib = UINib(nibName: nibName, bundle: nil)
    return nib.instantiateWithOwner(self, options: nil).first as! Self
  }

}

4
キャストする必要がないため、これは選択した回答よりも優れたソリューションであり、将来作成する他のUIViewサブクラス全体で再利用することもできます。
user3344977 2015年

3
私はSwift 2.1とXcode 7.2.1でこれを試しました。それは時々機能し、ミューテックスロックで他の人に予期せずハングアップしました。コードで直接使用された最後の2行はvar myView = nib.instantiate... as! myViewType
常に機能

@ jr-root-cs編集にタイプミス/エラーが含まれていたため、ロールバックする必要がありました。とにかく、既存の回答にコードを追加しないでください。代わりに、コメントしてください。または自分の回答にバージョン追加してください。ありがとう。
Eric Aya

私は自分のプロジェクトでSwift 3(XCode 8.0 beta 6)を使用して開いてテストしたコードを問題なく投稿しました。タイプミスはにありましたSwift 2。この回答が適切で、ユーザーがXC8を使用するときの変更点を検索するのを好む可能性があるのに、なぜそれが別の回答である必要があるのか
jr.root.cs

1
@ jr.root.csはい、この回答は適切です。そのため、誰も変更しないでください。これはあなたの答えではなく、サムの答えです。コメントしたい場合は、コメントを残してください。新しいバージョンまたは更新されたバージョンを投稿する場合は、自分の投稿で行ってください。編集は、タイプミス/インデント/タグを修正して、自分のバージョンを他の人の投稿に追加しないようにするためのものです。ありがとう。
Eric Aya

37

そして、これがSwift 3.0でのFrederikの答えです

/*
 Usage:
 - make your CustomeView class and inherit from this one
 - in your Xib file make the file owner is your CustomeView class
 - *Important* the root view in your Xib file must be of type UIView
 - link all outlets to the file owner
 */
@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

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

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

    private func nibSetup() {
        backgroundColor = .clear

        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return nibView
    }
}

31

xibからビューをロードする一般的な方法:

例:

let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)

実装:

extension Bundle {

    static func loadView<T>(fromNib name: String, withType type: T.Type) -> T {
        if let view = Bundle.main.loadNibNamed(name, owner: nil, options: nil)?.first as? T {
            return view
        }

        fatalError("Could not load view with type " + String(describing: type))
    }
}

UIViewサブクラスのタイプとしての出力ビューとしての私への最良の答え
jfgrang 2017年

26

Swift 3 Answer:私の場合、カスタムクラスに変更可能なアウトレットを用意したいと思いました。

class MyClassView: UIView {
    @IBOutlet weak var myLabel: UILabel!

    class func createMyClassView() -> MyClass {
        let myClassNib = UINib(nibName: "MyClass", bundle: nil)
        return myClassNib.instantiate(withOwner: nil, options: nil)[0] as! MyClassView
    }
}

.xibにある場合は、[カスタムクラス]フィールドがMyClassViewであることを確認します。ファイルの所有者に迷惑をかけないでください。

カスタムクラスがMyClassViewであることを確認します

また、MyClassViewのコンセントをラベルに接続していることを確認してください。 myLabelのアウトレット

それをインスタンス化するには:

let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"

所有者が設定されていない場合、「「MyClas」のnibがロードされましたが、ビューアウトレットが設定されていませんでした。」」が
返さ

22

スウィフト4

ここでは、カスタムビューにデータを渡す必要があるため、ビューをインスタンス化する静的関数を作成します。

  1. UIView拡張を作成する

    extension UIView {
        class func initFromNib<T: UIView>() -> T {
            return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
        }
    }
  2. MyCustomViewを作成する

    class MyCustomView: UIView {
    
        @IBOutlet weak var messageLabel: UILabel!
    
        static func instantiate(message: String) -> MyCustomView {
            let view: MyCustomView = initFromNib()
            view.messageLabel.text = message
            return view
        }
    }
  3. .xibファイルでカスタムクラスをMyCustomViewに設定します。必要に応じて、通常どおりコンセントを接続します。 ここに画像の説明を入力してください

  4. ビューをインスタンス化

    let view = MyCustomView.instantiate(message: "Hello World.")

カスタムビューにボタンがある場合、さまざまなビューコントローラーでボタンのアクションをどのように処理できますか?
jayant rawat

protocol-delegateを使用できます。こちらをご覧くださいstackoverflow.com/questions/29602612/…
ニーモニック

xibに画像を読み込めませんでした。次のエラーが発生しました。識別子が「test.Testing」のバンドル内のnibから参照される「IBBrokenImage」画像を読み込めませんでした
Ravi Raja Jangid

-4
override func draw(_ rect: CGRect) 
{
    AlertView.layer.cornerRadius = 4
    AlertView.clipsToBounds = true

    btnOk.layer.cornerRadius = 4
    btnOk.clipsToBounds = true   
}

class func instanceFromNib() -> LAAlertView {
    return UINib(nibName: "LAAlertView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! LAAlertView
}

@IBAction func okBtnDidClicked(_ sender: Any) {

    removeAlertViewFromWindow()

    UIView.animate(withDuration: 0.4, delay: 0.0, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)

    }, completion: {(finished: Bool) -> Void in
        self.AlertView.transform = CGAffineTransform.identity
        self.AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
        self.AlertView.isHidden = true
        self.AlertView.alpha = 0.0

        self.alpha = 0.5
    })
}


func removeAlertViewFromWindow()
{
    for subview  in (appDel.window?.subviews)! {
        if subview.tag == 500500{
            subview.removeFromSuperview()
        }
    }
}


public func openAlertView(title:String , string : String ){

    lblTital.text  = title
    txtView.text  = string

    self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
    appDel.window!.addSubview(self)


    AlertView.alpha = 1.0
    AlertView.isHidden = false

    UIView.animate(withDuration: 0.2, animations: {() -> Void in
        self.alpha = 1.0
    })
    AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)

    UIView.animate(withDuration: 0.3, delay: 0.2, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)

    }, completion: {(finished: Bool) -> Void in
        UIView.animate(withDuration: 0.2, animations: {() -> Void in
            self.AlertView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)

        })
    })


}

不正な形式のコード、説明がないため、反対票。
J. Doe

これは質問とどのような関係がありますか?
Ashley Mills
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.