web-dev-qa-db-fra.com

Comment initialiser / instancier une classe UIView personnalisée avec un fichier XIB dans Swift

J'ai une classe appelée MyClass, qui est une sous-classe de UIView, que je souhaite initialiser avec un fichier XIB. Je ne sais pas comment initialiser cette classe avec le fichier xib appelé View.xib

class MyClass: UIView {

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

J'ai testé ce code et cela fonctionne très bien

class MyClass: UIView {

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

}

Initialiser la vue et l'utiliser comme ci-dessous

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

OU

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

PDATE 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
}
240
Ezimet

La solution de Sam est déjà excellente, même si elle ne prend pas en compte différents bundles (NSBundle: forClass vient à la rescousse) et nécessite un chargement manuel, du code de frappe a.k.a.

Si vous souhaitez une prise en charge complète de vos prises Xib, différents bundles (à utiliser dans les frameworks!) Et un aperçu de Nice dans Storyboard, essayez ceci:

// NibLoadingView.Swift
import UIKit

// Usage: Subclass your UIView from NibLoadView to automatically load a xib with the same name as your class

@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
    }

}

Utilisez votre xib comme d'habitude, c'est-à-dire connectez les prises au propriétaire du fichier et définissez la classe du propriétaire du fichier sur votre propre classe.

Utilisation: Il suffit de sous-classer votre propre classe View de NibLoadingView.

Aucun code supplémentaire requis plus.

Crédits où le crédit est dû: Cela a été modifié avec des changements mineurs de DenHeadless sur GH. My Gist: https://Gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8

À partir de Swift 2.0, vous pouvez ajouter une extension de protocole. À mon avis, il s'agit d'une meilleure approche car le type de retour est Self plutôt que UIView, de sorte que l'appelant n'a pas besoin de transtyper vers la classe d'affichage.

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
  }

}
74
Sam

Et voici la réponse de Frederik sur Swift 3.0

@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
    }
}
36
Mahmood S

Mode de chargement universel vue depuis xib:

Exemple:

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

La mise en oeuvre:

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))
    }
}

Réponse de Swift 3: Dans mon cas, je voulais avoir un point de vente dans ma classe personnalisée que je puisse modifier:

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
    }
}

Lorsque vous êtes dans le fichier .xib, assurez-vous que le champ Classe personnalisée est MyClassView. Ne vous embêtez pas avec le propriétaire du fichier.

Make sure Custom Class is MyClassView

Assurez-vous également que vous connectez la prise de MyClassView à l’étiquette: Outlet for myLabel

Pour l'instancier:

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

Swift 4

Ici, dans mon cas, je dois passer des données dans cette vue personnalisée, je crée donc une fonction statique pour instancier la vue.

  1. Créer une extension UIView

    extension UIView {
        class func initFromNib<T: UIView>() -> T {
            return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
        }
    }
    
  2. Créer 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. Définissez la classe personnalisée sur MyCustomView dans un fichier .xib. Connectez la prise si nécessaire comme d'habitude. enter image description here

  4. Instancier la vue

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

Il existe quelques frameworks, qui implémentent le même code qui est mentionné à plusieurs reprises sur cette page.

Je publie un de ces cadres, LoadableViews .

Et puisque ce cadre est déjà utilisé dans presque toutes les applications de MLSDev, la société sur laquelle je travaille, il sera activement pris en charge dans un avenir prévisible.

Il suffit de poster ici pour quiconque est intéressé.

3
DenHeadless