web-dev-qa-db-fra.com

Charger un UIView de nib dans Swift

Voici mon code Objective-C que j'utilise pour charger une plume pour ma UIView personnalisée:

-(id)init{

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

}

Quel est le code équivalent dans Swift?

114
Bagusflyer

Solution originale

  1. J'ai créé un fichier XIB et une classe nommée SomeView (le même nom a été utilisé pour la commodité et la lisibilité). Je me suis basé à la fois sur un UIView.
  2. Dans XIB, j'ai changé la classe "File's Owner" en SomeView (dans l'inspecteur d'identité).
  3. J'ai créé une sortie UIView dans SomeView.Swift, en la liant à la vue de niveau supérieur du fichier XIB (nommée "vue" pour plus de commodité). J'ai ensuite ajouté d'autres points de vente à d'autres contrôles dans le fichier XIB selon les besoins.
  4. dans SomeView.Swift, j'ai chargé le XIB dans l'initialiseur "init with code". Il n'y a pas besoin d'attribuer quoi que ce soit à "soi". Dès que le fichier XIB est chargé, tous les points de vente sont connectés, y compris la vue de niveau supérieur. La seule chose qui manque, c'est d'ajouter la vue de dessus à la hiérarchie de vues:

.

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

Notez que de cette façon je reçois une classe qui se charge de nib. Je pourrais ensuite utiliser SomeView en tant que classe chaque fois que UIView pourrait être utilisé dans le projet (dans le générateur d'interface ou par programme).

Update - en utilisant la syntaxe Swift 3

Le chargement d'un xib dans l'extension suivante s'écrit comme une méthode d'instance, qui peut ensuite être utilisée par un initialiseur comme celui ci-dessus:

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. L'utilisation d'une valeur de retour pouvant être éliminée étant donné que la vue renvoyée n'a aucun intérêt pour l'appelant lorsque toutes les prises sont déjà connectées.
  2. Il s'agit d'une méthode générique qui renvoie un objet facultatif de type UIView. Si le chargement de la vue échoue, il renvoie nil.
  3. Tentative de chargement d'un fichier XIB portant le même nom que l'instance de classe actuelle. Si cela échoue, nil est renvoyé.
  4. Ajout de la vue de niveau supérieur à la hiérarchie de vues.
  5. Cette ligne suppose que nous utilisons des contraintes pour mettre en forme la vue.
  6. Cette méthode ajoute des contraintes de haut, de bas, de début et de fin - associe la vue à "self" de tous les côtés (voir: https://stackoverflow.com/a/46279424/2274829 pour plus de détails)
  7. Retour de la vue de niveau supérieur

Et la méthode de l'appelant pourrait ressembler à ceci:

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 est une sous-classe UIView qui charge son contenu à partir d'un fichier SomeClass.xib. Le mot clé "final" est facultatif.
  2. Un initialiseur pour quand la vue est utilisée dans un storyboard (n'oubliez pas d'utiliser SomeClass comme classe personnalisée de votre vue de storyboard).
  3. Un initialiseur pour quand la vue est créée par programme (c'est-à-dire: "let myView = SomeView ()"). 
  4. Utilisation d'un cadre contenant uniquement des zéros, car cette vue est présentée à l'aide de la disposition automatique . Notez qu'une méthode "init (frame: CGRect) {..}" n'est pas créée indépendamment, car la disposition automatique est utilisée exclusivement dans notre environnement. projet.
  5. & 6. Chargement du fichier xib en utilisant l’extension.

Crédit: L'utilisation de l'extension générique dans cette solution a été inspirée par la réponse de Robert ci-dessous. 

Edit Changer "view" en "contentView" pour éviter toute confusion. Également changé l'indice de tableau en ".first".

145
GK100

Ma contribution:

Swift 3/Swift 4

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

Puis appelle ça comme ça:

let myCustomView: CustomView = UIView.fromNib()

..ou même:

let myCustomView: CustomView = .fromNib()
217
Robert Gummesson

Voici une extension qui utilise des génériques pour charger une UIView à partir d'une nib

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

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

    public class func fromNib<T : UIView>(nibNameOrNil: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String
        if let nibName = nibNameOrNil {
            name = nibName
        } else {
            // Most nibs are demangled by practice, if not, just declare string explicitly
            name = nibName
        }
        let nibViews = NSBundle.mainBundle().loadNibNamed(name, owner: nil, options: nil)
        for v in nibViews {
            if let tog = v as? T {
                view = tog
            }
        }
        return view
    }

    public class var nibName: String {
        let name = "\(self)".componentsSeparatedByString(".").first ?? ""
        return name
    }
    public class var nib: UINib? {
        if let _ = NSBundle.mainBundle().pathForResource(nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: nil)
        } else {
            return nil
        }
    }
}

Je préfère cela car il ne nécessite aucune configuration supplémentaire dans la plume. Il est basé sur les conventions de nommage générales, donc si votre classe est CustomView et qu'elle correspond à une nib nommée: CustomView, vous pouvez simplement faire ceci:

let myCustomView = CustomView.fromNib()
// or if you're unsure whether or not the nib exists
let myCustomView: CustomView? = CustomView.fromNib()

Si vous devez préciser le nom du nib pour une raison quelconque, passez un argument de chaîne:

let myCustomView = MyCustomView.fromNib("non-conventional-name")

Problèmes connus 

Son utilisation avec une classe de vue privée semble poser problème. Cela semble être un problème à l’échelle du système.

76
Logan

essayez de suivre le code. 

var uiview :UIView?

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

Modifier:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

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


}
15
Jasmin

J'ai réalisé cela avec Swift avec le code suivant:

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

N'oubliez pas de connecter votre XIB view sortie à la sortie view définie dans Swift. Vous pouvez également définir First Responder sur votre nom de classe personnalisé pour commencer à connecter des prises supplémentaires.

J'espère que cela t'aides!

10
Amro Shafie

Testé dans Xcode 7 beta 4, Swift 2.0 et iOS9 SDK. Le code suivant assignera xib à uiview . Vous pourrez utiliser cette vue xib personnalisée dans le storyboard et accéder également à l'objet 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);



    }


}

Accéder à la vue personnalisée par programme

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

Code source - https://github.com/karthikprabhuA/CustomXIBSwift

9
karthikPrabhu Alagu

Construire sur les solutions ci-dessus.

Cela fonctionnera pour tous les lots de projets et aucun besoin de génériques lors de l'appel deNib ().

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

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

Peut être utilisé comme ceci:

let someView = SomeView.fromNib()

Ou comme ceci:

let someView = SomeView.fromNib("SomeOtherNibFileName")
7
Genesis

Si vous avez beaucoup de vues personnalisées dans votre projet, vous pouvez créer une classe comme 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)
    }
}

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

Et dans chaque classe, héritez simplement de UIViewFromNib, vous pouvez également remplacer la propriété nibName si le fichier .xib a un nom différent:

class MyCustomClass: UIViewFromNib {

}
7
ChikabuZ

Une bonne façon de faire cela avec Swift est d’utiliser une énumération.

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)[0] as! UIView
    }
}

Ensuite, dans votre code, vous pouvez simplement utiliser:

let view = Views.view1.getView()
5
totiG

Extensions de protocole 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

    }

}

Adoption

class MyView: UIView, NibInstantiatable {

}

Cette implémentation suppose que Nib a le même nom que la classe UIView. Ex. MyView.xib. Vous pouvez modifier ce comportement en implémentant nibName () dans MyView pour renvoyer un nom différent de celui de l'extension de protocole par défaut.

Dans la bibliothèque xib, le propriétaire des fichiers est MyView et la classe de vue racine est MyView.

Utilisation

let view = MyView.fromNib()
4
Brody Robertson

Swift 3 version de la réponse de Logan

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
Alexey Karetski

Je préfère cette solution (basée sur la réponse si @ GK100): 

  1. J'ai créé un fichier XIB et une classe nommée SomeView (le même nom a été utilisé pour des raisons de commodité et de lisibilité). Je me suis basé à la fois sur un UIView.
  2. Dans XIB, j'ai changé la classe "File's Owner" en SomeView (dans l'inspecteur d'identité).
  3. J'ai créé une sortie UIView dans SomeView.Swift, en la liant à la vue de niveau supérieur du fichier XIB (nommée "vue" pour plus de commodité). J'ai ensuite ajouté d'autres points de vente à d'autres contrôles dans le fichier XIB selon les besoins.
  4. Dans SomeView.Swift, j'ai chargé le fichier XIB dans l'initialiseur init ou init:frame: CGRect. Il n'y a pas besoin d'attribuer quoi que ce soit à "soi". Dès que le fichier XIB est chargé, tous les points de vente sont connectés, y compris la vue de niveau supérieur. La seule chose qui manque, c'est d'ajouter la vue de dessus à la hiérarchie de vues:

    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
      }
    
    
      ...
    }
    
3
confile
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]
3
BaSha

Voici une méthode propre et déclarative de chargement par programme d'une vue à l'aide d'un protocole et d'une extension de protocole (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
    }
}

Et vous pouvez utiliser ceci comme ceci:

// 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()

Quelques considérations supplémentaires :

  1. Assurez-vous que le fichier xib de votre vue personnalisée possède le Custom Class de la vue (et les prises/actions définies à partir de là), et non le propriétaire du fichier.
  2. Vous pouvez utiliser ce protocole/cette extension externe à votre vue personnalisée ou interne. Vous voudrez peut-être l'utiliser en interne si vous avez d'autres tâches d'installation lors de l'initialisation de votre vue.
  3. Votre classe de vue personnalisée et votre fichier xib doivent avoir le même nom.
2
jason z

Tout ce que vous avez à faire est d’appeler la méthode init dans votre classe UIView.

Faites-le comme ça:

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

Maintenant, si vous souhaitez ajouter cette vue en tant que sous-vue dans le contrôleur de vue, procédez de cette manière dans le fichier view controller.Swift:

self.view.addSubview(className())
2
Alap Anerao

Swift 4

N'oubliez pas d'écrire ".first as? CustomView". 

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

Si vous voulez utiliser n'importe où 

La meilleure solution est la réponse de 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
    }
}

Puis appelle ça comme ça:

let myCustomView: CustomView = UIView.fromNib()
1
codeByThey

Vous pouvez le faire via un scénario, en ajoutant simplement les contraintes appropriées pour la vue. Vous pouvez le faire facilement en sous-classant une vue de votre propre, disons BaseView:

Objectif 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

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

Je propose 2 variantes pour ajouter des contraintes - communes et dans un langage de format visuel - sélectionnez celles que vous voulez :)

De plus, supposons par défaut que xib name ait le même nom que le nom de la classe d'implémentation. Si non, changez simplement le paramètre xibName.

Si vous sous-classez votre vue à partir de BaseView, vous pouvez facilement placer n'importe quelle vue et spécifier une classe dans IB.

1
gbk

Je fais juste comme ça:

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

Cet exemple utilise la première vue de la nib "MyView.xib" dans le bundle principal. Mais vous pouvez modifier l’index, le nom du nib ou le bundle (main par défaut).

J'avais l'habitude d'éveiller des vues dans la méthode view init ou de créer des méthodes génériques comme dans les solutions ci-dessus (qui sont intelligentes d'ailleurs), mais je ne le fais plus.

De cette façon, je peux utiliser différentes dispositions ou caractéristiques tout en conservant la même logique de vue et le même code.

Je trouve plus facile de laisser un objet fabrique (généralement le viewController qui utilisera la vue) le crée quand il en a besoin. Parfois, vous avez besoin d'un propriétaire (généralement lorsque la vue créée a un point de vente connecté au créateur), parfois pas ..

C'est probablement pourquoi Apple n'a pas inclus de méthode initFromNib dans sa classe UIView ...

Pour prendre un exemple au niveau du sol, vous ne savez pas comment vous êtes né. Vous venez de naître. Ainsi sont les vues;)

1
Moose

Semblable à certaines des réponses ci-dessus mais avec une extension plus cohérente de 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)
    }
}

Ce qui donne l’opportunité de pouvoir charger la classe à partir d’une plume auto-nommée mais aussi d’autres plumes/bundles.

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

Si vous voulez que la sous-classe de Swift UIView soit entièrement autonome et que vous puissiez être instanciée à l'aide de init ou init (frame :) sans exposer les détails d'implémentation de l'utilisation d'un Nib, vous pouvez utiliser une extension de protocole pour y parvenir. Cette solution évite la hiérarchie imbriquée UIView comme suggéré par de nombreuses autres solutions.

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
appfigurate

L'implémentation la plus pratique. Ici, vous avez besoin de deux méthodes pour retourner directement à l'objet de votre classe, pas à UIView.

  1. viewId marqué en tant que classe , autorisant le remplacement 
  2. Votre fichier .xib peut contenir plusieurs vues du niveau supérieur, cette situation est également gérée correctement.

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

Exemple:

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

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

Version plus puissante basée sur la réponse de Logan

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

Et vous pouvez utiliser comme 

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
user2790103