webentwicklung-frage-antwort-db.com.de

Laden Sie eine UIView von der Feder in Swift

Hier ist mein Objective-C-Code, den ich zum Laden einer Spitze für mein benutzerdefiniertes UIView-Objekt verwende:

-(id)init{

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

}

Was ist der äquivalente Code in Swift?

114
Bagusflyer

Originallösung

  1. Ich habe eine XIB und eine Klasse namens SomeView erstellt (benannte denselben Namen für Benutzerfreundlichkeit und Lesbarkeit). Ich habe beide auf einem UIView basiert.
  2. In der XIB habe ich die Klasse "Besitzer der Datei" in SomeView (im Identitätsinspektor) geändert.
  3. Ich habe ein UIView-Outlet in SomeView.Swift erstellt und es mit der Ansicht der obersten Ebene in der XIB-Datei verlinkt (die "Bequemlichkeit" genannt wird). Ich habe dann bei Bedarf weitere Auslässe zu anderen Steuerelementen in der XIB-Datei hinzugefügt.
  4. in SomeView.Swift habe ich die XIB in den Initialisierer "init with code" geladen. Es ist nicht notwendig, "Selbst" etwas zuzuweisen. Sobald der XIB geladen ist, sind alle Ausgänge verbunden, einschließlich der Ansicht der obersten Ebene. Das einzige, was fehlt, ist das Hinzufügen der Draufsicht zur Ansichtshierarchie:

.

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

Beachten Sie, dass ich auf diese Weise eine Klasse bekomme, die sich von der Spitze lädt. Ich könnte SomeView dann als Klasse verwenden, wenn UIView im Projekt verwendet werden kann (im Interface-Builder oder programmgesteuert).

Update - mit der Swift 3-Syntax

Das Laden eines Xibs in der folgenden Erweiterung wird als Instanzmethode geschrieben, die dann von einem Initialisierer wie dem obigen verwendet werden kann:

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. Die Verwendung eines verwerfbaren Rückgabewerts, da die zurückgegebene Ansicht für den Anrufer meist nicht von Interesse ist, wenn alle Verkaufsstellen bereits verbunden sind.
  2. Dies ist eine generische Methode, die ein optionales Objekt vom Typ UIView zurückgibt. Wenn die Ansicht nicht geladen wird, wird null zurückgegeben.
  3. Versuch, eine XIB-Datei mit demselben Namen wie die aktuelle Klasseninstanz zu laden. Wenn dies fehlschlägt, wird nil zurückgegeben.
  4. Hinzufügen der Ansicht der obersten Ebene zur Ansichtshierarchie.
  5. In dieser Zeile wird davon ausgegangen, dass wir Einschränkungen für das Layout der Ansicht verwenden.
  6. Diese Methode fügt obere, untere, führende und nachstehende Einschränkungen hinzu - indem Sie die Ansicht auf allen Seiten "self" hinzufügen (siehe: https://stackoverflow.com/a/46279424/2274829 für Details).
  7. Rückkehr zur Ansicht der obersten Ebene

Und die Aufrufermethode könnte so aussehen:

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 ist eine UIView-Unterklasse, die ihren Inhalt aus einer SomeClass.xib-Datei lädt. Das Schlüsselwort "final" ist optional.
  2. Eine Initialisierung für die Verwendung der Ansicht in einem Storyboard (Denken Sie daran, SomeClass als benutzerdefinierte Klasse Ihrer Storyboard-Ansicht zu verwenden).
  3. Ein Initialisierer, wenn die Ansicht programmgesteuert erstellt wird (d. H. "Let myView = SomeView ()"). 
  4. Verwenden eines Rahmens ohne Nullen, da diese Ansicht mit auto-layout ..__ angelegt ist. Beachten Sie, dass die Methode "init (frame: CGRect) {..}" nicht unabhängig erstellt wird, da in unserem System ausschließlich das auto-layout verwendet wird Projekt.
  5. & 6. Laden der xib-Datei mit der Erweiterung.

Bildnachweis: Die Verwendung einer generischen Erweiterung in dieser Lösung wurde von Roberts Antwort unten inspiriert. 

Edit Ändern von "view" in "contentView", um Verwirrung zu vermeiden. Der Array-Index wurde in ".first" geändert.

145
GK100

Mein Beitrag:

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

Dann nennen Sie es so:

let myCustomView: CustomView = UIView.fromNib()

..oder auch:

let myCustomView: CustomView = .fromNib()
217

Hier ist eine Erweiterung, die Generics zum Laden eines UIView aus einer Spitze verwendet

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

Ich bevorzuge das, da es keine zusätzlichen Einstellungen in der Spitze erfordert. Es basiert auf allgemeinen Namenskonventionen. Wenn Ihre Klasse CustomView ist und mit einer Spitze mit dem Namen CustomView übereinstimmt, können Sie Folgendes tun:

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

Wenn Sie aus irgendeinem Grund den Namen der Spitze angeben müssen, übergeben Sie eine Zeichenfolge arg:

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

Bekannte Probleme 

Die Verwendung dieser Klasse mit einer privaten Ansichtsklasse scheint Probleme zu verursachen. Dies scheint ein systemweites Problem zu sein.

76
Logan

versuchen Sie folgenden Code. 

var uiview :UIView?

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

Bearbeiten:

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

Ich habe dies mit Swift durch folgenden Code erreicht:

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

Vergessen Sie nicht, Ihren XIB view outlet mit dem in Swift definierten view - Ausgang zu verbinden. Sie können First Responder auch auf Ihren benutzerdefinierten Klassennamen setzen, um weitere zusätzliche Ausgänge anzuschließen.

Hoffe das hilft!

10
Amro Shafie

Getestet in Xcode 7 Beta 4, Swift 2.0 und iOS9 SDK. Der folgende Code weist der uiview xib zu. . Sie können diese benutzerdefinierte Xib-Ansicht im Storyboard verwenden und auch auf das IBOutlet-Objekt zugreifen.

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



    }


}

Greifen Sie programmgesteuert auf customview zu

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

Quellcode - https://github.com/karthikprabhuA/CustomXIBSwift

9

Aufbauend auf den oben genannten Lösungen.

Dies funktioniert über alle Projektbündel hinweg und es sind keine Generika erforderlich, wenn fromNib () aufgerufen wird.

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

Kann wie folgt verwendet werden:

let someView = SomeView.fromNib()

Oder so:

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

Wenn Sie viele benutzerdefinierte Ansichten in Ihrem Projekt haben, können Sie Klassen wie UIViewFromNib erstellen.

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

In jeder Klasse, die nur von UIViewFromNib erbt, können Sie auch die nibName-Eigenschaft überschreiben, wenn die .xib-Datei einen anderen Namen hat:

class MyCustomClass: UIViewFromNib {

}
7
ChikabuZ

Eine gute Möglichkeit, dies mit Swift zu tun, ist die Verwendung einer Aufzählung.

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

Dann können Sie in Ihrem Code einfach Folgendes verwenden:

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

Swift 4-Protokollerweiterungen

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

    }

}

Annahme

class MyView: UIView, NibInstantiatable {

}

Diese Implementierung setzt voraus, dass die Nib den gleichen Namen wie die UIView-Klasse hat. Ex. MyView.xib. Sie können dieses Verhalten ändern, indem Sie nibName () in MyView implementieren, um einen anderen Namen als die Implementierung der Standardprotokollerweiterung zurückzugeben.

In der xib ist der Eigentümer der Dateien MyView und die Stammsichtklasse MyView.

Verwendungszweck

let view = MyView.fromNib()
4
Brody Robertson

Swift 3 Version von Logans Antwort

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

Ich bevorzuge diese Lösung (basierend auf der Antwort if @ GK100): 

  1. Ich habe eine XIB und eine Klasse namens SomeView erstellt (zur Vereinfachung und Lesbarkeit wurde derselbe Name verwendet). Ich habe beide auf einem UIView basiert.
  2. In der XIB habe ich die Klasse "Besitzer der Datei" in SomeView (im Identitätsinspektor) geändert.
  3. Ich habe ein UIView-Outlet in SomeView.Swift erstellt und es mit der Ansicht der obersten Ebene in der XIB-Datei verlinkt (die "Bequemlichkeit"). Ich habe dann bei Bedarf weitere Auslässe zu anderen Steuerelementen in der XIB-Datei hinzugefügt.
  4. In SomeView.Swift habe ich die XIB in den Initialisierer init oder init:frame: CGRect geladen. Es ist nicht notwendig, "Selbst" etwas zuzuweisen. Sobald der XIB geladen ist, sind alle Ausgänge verbunden, einschließlich der Ansicht der obersten Ebene. Das einzige, was fehlt, ist das Hinzufügen der Draufsicht zur Ansichtshierarchie:

    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

Hier ist eine saubere und deklarative Methode zum programmgesteuerten Laden einer Ansicht mithilfe einer Protokoll- und Protokollerweiterung (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
    }
}

Und das kannst du so verwenden:

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

Einige zusätzliche Überlegungen :

  1. Stellen Sie sicher, dass für die Xib-Datei Ihrer benutzerdefinierten Ansicht der Custom Class der Ansicht (und die von dort festgelegten Ausgänge/Aktionen) festgelegt ist, nicht für den Dateieigentümer.
  2. Sie können dieses Protokoll/diese Erweiterung extern für Ihre benutzerdefinierte Ansicht oder für intern verwenden. Möglicherweise möchten Sie es intern verwenden, wenn Sie beim Initialisieren Ihrer Ansicht andere Einrichtungsarbeit haben.
  3. Ihre benutzerdefinierte Ansichtsklasse und XIB-Datei müssen denselben Namen haben.
2
jason z

Sie müssen lediglich die init-Methode in Ihrer UIView-Klasse aufrufen.

Mach es so:

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

Wenn Sie diese Ansicht jetzt als Unteransicht im Ansichtscontroller hinzufügen möchten, tun Sie dies in der Ansichtscontroller.Swift-Datei folgendermaßen:

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

Swift 4

Vergessen Sie nicht, ".first as? CustomView" zu schreiben. 

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

Wenn Sie irgendwo verwenden möchten 

Die beste Lösung ist die Antwort von 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
    }
}

Dann nennen Sie es so:

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

Sie können dies über das Storyboard tun, indem Sie nur die richtigen Einschränkungen für die Ansicht hinzufügen. Sie können dies ganz einfach tun, indem Sie eine beliebige Ansicht aus Ihrer eigenen Sicht subklassieren, sagen wir BaseView:

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

Ich biete zwei Varianten an, wie man Einschränkungen hinzufügt - üblich und innerhalb der Sprache des visuellen Formats - jede beliebige auswählen :)

Außerdem wird standardmäßig davon ausgegangen, dass xib name denselben Namen wie der Name der Implementierungsklasse hat. Wenn nein, ändern Sie einfach den Parameter xibName.

Wenn Sie Ihre Sicht von BaseView subclassieren, können Sie leicht jede Ansicht einfügen und die Klasse in IB angeben.

1
gbk

Ich mache einfach so:

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

In diesem Beispiel wird die erste Ansicht in der Spitze "MyView.xib" im Hauptpaket verwendet. Sie können jedoch entweder den Index, den Namen der Spitze oder das Bundle (standardmäßig main) ändern.

Früher habe ich Einblicke in die View-Init-Methode geweckt oder generische Methoden wie in den obigen Lösungen (die übrigens intelligent sind) erstellt, aber ich mache es nicht mehr.

Auf diese Weise kann ich unterschiedliche Layouts oder Merkmale verwenden, wobei die gleiche Ansichtslogik und derselbe Code beibehalten werden.

Ich finde es einfacher, ein Factory-Objekt (normalerweise der viewController, der die Ansicht verwendet) erstellen zu lassen, wenn es benötigt wird. Manchmal benötigen Sie einen Eigentümer (in der Regel, wenn die erstellte Ansicht mit dem Ersteller verbunden ist), manchmal nicht.

Das ist wahrscheinlich der Grund, warum Apple keine initFromNib-Methode in seine UIView-Klasse aufgenommen hat ...

Um ein Beispiel aus dem Boden zu nehmen, wissen Sie nicht, wie Sie geboren werden. Du bist gerade geboren. So sind die Ansichten;)

1
Moose

Ähnlich wie einige der obigen Antworten, jedoch eine konsistentere Swift3 UIView-Erweiterung:

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

Dies bietet den Komfort, die Klasse von einer selbst benannten Feder, aber auch von anderen Federn/Bündeln laden zu können.

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

Wenn Sie möchten, dass die Swift-UIView-Unterklasse vollständig in sich geschlossen ist und die Möglichkeit besteht, mit init oder init (frame :) instanziiert zu werden, ohne die Implementierungsdetails der Verwendung einer Nib preiszugeben, können Sie dies mit einer Protokollerweiterung tun. Diese Lösung vermeidet die verschachtelte UIView-Hierarchie, wie von vielen anderen Lösungen vorgeschlagen.

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

Die bequemste Implementierung. Hier benötigen Sie zwei Methoden, um direkt zum Objekt Ihrer Klasse zurückzukehren, nicht zu UIView.

  1. viewId ist als class markiert und kann überschrieben werden 
  2. Ihre .xib kann mehrere Ansichten der obersten Ebene enthalten. Diese Situation wird auch korrekt behandelt.

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

Beispiel:

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

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

Stärkere Version basierend auf Logans Antwort

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

Und du kannst gerne verwenden 

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