Carica un UIView dal pennino in Swift


148

Ecco il mio codice Objective-C che sto usando per caricare un pennino per il mio personalizzato UIView:

-(id)init{

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

}

Qual è il codice equivalente in Swift?

Risposte:


172

Soluzione originale

  1. Ho creato un XIB e una classe denominata SomeView (usato lo stesso nome per praticità e leggibilità). Ho basato entrambi su una UIView.
  2. In XIB, ho modificato la classe "File's Owner" in SomeView (nella finestra di ispezione identità).
  3. Ho creato un punto vendita UIView in SomeView.swift, collegandolo alla vista di livello superiore nel file XIB (per comodità è stato chiamato "view"). Ho quindi aggiunto altri punti vendita ad altri controlli nel file XIB secondo necessità.
  4. in SomeView.swift, ho caricato l'XIB all'interno dell'inizializzatore "init con codice". Non è necessario assegnare nulla a "sé". Non appena viene caricato XIB, tutte le prese vengono collegate, inclusa la vista di livello superiore. L'unica cosa che manca è aggiungere la vista dall'alto alla gerarchia della vista:

.

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

Nota che in questo modo ottengo una classe che si carica dal pennino. Potrei quindi utilizzare SomeView come classe ogni volta che UIView potrebbe essere utilizzato nel progetto (nel generatore di interfaccia o a livello di codice).

Aggiornamento - usando la sintassi di Swift 3

Il caricamento di un xib nella seguente estensione è scritto come un metodo di istanza, che può quindi essere utilizzato da un inizializzatore come quello sopra:

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'uso di un valore di ritorno scartabile poiché la vista restituita non è per lo più di interesse per il chiamante quando tutti i punti vendita sono già connessi.
  2. Questo è un metodo generico che restituisce un oggetto opzionale di tipo UIView. Se non riesce a caricare la vista, restituisce zero.
  3. Tentativo di caricare un file XIB con lo stesso nome dell'istanza di classe corrente. In caso contrario, viene restituito zero.
  4. Aggiunta della vista di livello superiore alla gerarchia della vista.
  5. Questa riga presuppone che stiamo utilizzando i vincoli per il layout della vista.
  6. Questo metodo aggiunge i vincoli superiore, inferiore, iniziale e finale - allegando la vista a "sé" su tutti i lati (vedere: https://stackoverflow.com/a/46279424/2274829 per i dettagli)
  7. Restituzione della vista di livello superiore

E il metodo del chiamante potrebbe apparire così:

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 è una sottoclasse UIView che carica il suo contenuto da un file SomeClass.xib. La parola chiave "finale" è facoltativa.
  2. Un inizializzatore per quando la vista viene utilizzata in uno storyboard (ricorda di utilizzare SomeClass come classe personalizzata della visualizzazione dello storyboard).
  3. Un inizializzatore per quando la vista viene creata a livello di codice (ad esempio: "let myView = SomeView ()").
  4. Utilizzo di una cornice di zeri poiché questa vista è strutturata mediante il layout automatico. Si noti che un metodo "init (frame: CGRect) {..}" non viene creato in modo indipendente, poiché il layout automatico viene utilizzato esclusivamente nel nostro progetto.
  5. & 6. Caricamento del file xib utilizzando l'estensione.

Credito: l'utilizzo di un'estensione generica in questa soluzione è stato ispirato dalla risposta di Robert di seguito.

Modifica Modifica "view" in "contentView" per evitare confusione. Anche cambiato il pedice dell'array in ".first".


7
Impostazione del nome della classe per File's Ownerraggiungere il punto ... Grazie!
Aviel Gross

13
UIView non ha una vista delle proprietà, quindi chiamare self.view provoca un errore
Nastya Gorban,

4
@NastyaGorban self.view in realtà si riferisce in questo caso alla proprietà outlet (denominata "view) che GK100 ha collegato dalla vista di livello superiore in .xib a SomeView.swift. Non aggiungendo tale outlet si genererà un errore poiché non è presente una vista" "proprietà nelle classi NSView come dici tu.
Ausiàs

3
Ricevo un arresto anomalo durante il caricamento del pennino (loadNibNamed). Usando Xcode 6.3 e Swift
karthikPrabhu Alagu il

11
la chiamata fromNib()dall'interno init(coder aDecoder: NSCoder)crea un ciclo infinito mentre il caricamento del pennino all'interno del fromNib()metodo effettua una chiamata a:init(coder aDecoder: NSCoder)
Matthew Cawley il

334

Il mio contributo:

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

Quindi chiamalo così:

let myCustomView: CustomView = UIView.fromNib()

..o anche:

let myCustomView: CustomView = .fromNib()

20
La migliore risposta di gran lunga.
CodyMace,

7
La migliore risposta qui. Clean and Simple
Marquavious Draggon

3
@YuchenZhong - Preferisco [0] rispetto a .sir, in quanto restituirebbe un facoltativo. Se lo costringi a scartarlo, non sarebbe più sicuro. ... e questo pone la domanda: perché non restituire un optional come alcune delle soluzioni sopra? Risposta: puoi. Niente di sbagliato in questo. Ma ... se mai restituisse zero, il nome della classe xib / non corrisponde. Questo è un errore dello sviluppatore e dovrebbe essere colto immediatamente e non arrivare mai in produzione. Qui preferirei che l'app si arrestasse in modo anomalo lasciandola in uno stato strano. Solo i miei 2 centesimi / preferenza.
Robert Gummesson,

1
@allenlinli - Il metodo è un'estensione statica di UIView come dovrebbe CustomView. Funziona perché il compilatore inserisce il tipo usando l'annotazione esplicita del tipo. Poiché CustomView è una sottoclasse di UIView e il tipo è già stato dedotto, non è necessario inferirlo di nuovo, pertanto UIView può essere omesso come mostrato nel mio secondo esempio. Detto questo, puoi ovviamente effettuare la chiamata anche nel modo in cui la metti giù.
Robert Gummesson,

3
Questa soluzione non ha funzionato per me nel caso in cui all'interno di .xib fosse presente una vista personalizzata. Vorrei suggerire di correggere questa parte in: return Bundle.main.loadNibNamed (String (che descrive: self), proprietario: zero, opzioni: zero)! [0] as! T
Nadzeya,

79

Ora essere in grado di tornare -> Selfrapidamente aiuta a semplificare un po 'questo. Ultima conferma su 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
    }
}

Se il tuo .xibfile e la sottoclasse condividono lo stesso nome, puoi utilizzare:

let view = CustomView.fromNib()

Se hai un nome personalizzato, usa:

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

NOTA:

Se viene visualizzato l'errore "vista di tipo YourType non trovata in ..", non è stata impostata la classe della vista in .xib file

Seleziona la tua vista nel .xibfile e premi cmd + opt + 4e classnell'input, inserisci la tua classe


1
Non riesco a farlo funzionare con XCode 7.1 beta 3 - non sono sicuro che sia una cosa beta, ma fondamentalmente ho provato ogni modo per creare una vista personalizzata direttamente da un pennino in Swift e ottengo sempre lo stesso risultato: la classe che sta creando KVC non è conforme alle prese. Non sono sicuro che sia qualcosa che sto sbagliando, ma la mia classe è piuttosto semplice e il proprietario del file è corretto. Lo facevo sempre con Objective-C.
Echelon,

1
@Logan non è realmente correlato al tuo codice, ma le visualizzazioni personalizzate imo dovrebbero supportare il caricamento da Storyboard / XIB. Il mio commento è stato solo una notifica per coloro che vogliono creare tali punti di vista
Nikita ha preso il

1
Nota Ho ancora un problema nell'usare la seconda forma di chiamata a questa funzione, vale a dire let myCustomView = UIView.fromNib() as? CustomView. In questo caso, T.selfrisolve UIViewpiuttosto che CustomViewnon riesce a trovare il pennino. Non sono sicuro del perché - forse il tipo inferito per i letmezzi che la funzione viene chiamata come UIView?
Echelon,

2
È importante sottolineare che il tentativo di utilizzare il proprietario del file per collegare i punti vendita (come abbiamo fatto in passato) causerà un arresto anomalo. In IB, il proprietario del file deve essere nullo / vuoto e le prese devono essere collegate alla vista.
Echelon,

1
@Echelon mi hai salvato la giornata !!! Ho collegato i miei punti vendita usando il proprietario del file e non ha funzionato, usando invece la vista ha funzionato.
Jan Schlorf,

19

prova a seguire il codice.

var uiview :UIView?

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

Modificare:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

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


}

Devo chiamare questo metodo all'interno del metodo di inizializzazione dell'oggetto che è init () in Swift.
Bagusflyer,

12

Estensioni del protocollo 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

    }

}

Adozione

class MyView: UIView, NibInstantiatable {

}

Questa implementazione presuppone che il pennino abbia lo stesso nome della classe UIView. Ex. MyView.xib. È possibile modificare questo comportamento implementando nibName () in MyView per restituire un nome diverso rispetto all'implementazione dell'estensione del protocollo predefinita.

In xib il proprietario dei file è MyView e la classe di visualizzazione principale è MyView.

uso

let view = MyView.fromNib()

3
Questa è di gran lunga la soluzione più elegante e semplice e non ho idea del perché non sia la risposta accettata!
ferro di

@ horseshoe7 perché è scritto 4 anni dopo la domanda.
Freeubi

11

Ho raggiunto questo risultato con Swift con il seguente codice:

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

Non dimenticare di collegare la tua presa vista XIB per visualizzare la presa definita in breve tempo. È inoltre possibile impostare First Responder sul nome della propria classe personalizzata per iniziare a collegare eventuali punti vendita aggiuntivi.

Spero che questo ti aiuti!


10

Testato su Xcode 7 beta 4, Swift 2.0 e iOS9 SDK. Il codice seguente assegnerà xib a uiview. Puoi utilizzare questa vista xib personalizzata nello storyboard e accedere anche all'oggetto 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);



    }


}

Accedi alla visualizzazione personalizzata a livello di codice

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

Codice sorgente - https://github.com/karthikprabhuA/CustomXIBSwift


9

Se hai molte visualizzazioni personalizzate nel tuo progetto, puoi creare classi simili 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)
    }
}

E in ogni classe appena ereditata UIViewFromNib, puoi anche sovrascrivere la nibNameproprietà se il .xibfile ha un nome diverso:

class MyCustomClass: UIViewFromNib {

}

8

Basandosi sulle soluzioni di cui sopra.

Funzionerà su tutti i bundle di progetto e non sarà necessario avere generici quando si chiama fromNib ().

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

Può essere usato in questo modo:

let someView = SomeView.fromNib()

O così:

let someView = SomeView.fromNib("SomeOtherNibFileName")

6

Swift 4

Non dimenticare di scrivere ".prima come? CustomView".

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

Se vuoi usare ovunque

La migliore soluzione è la risposta di 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
    }
}

Quindi chiamalo così:

let myCustomView: CustomView = UIView.fromNib()

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

Ma in init () di Swift, non vi è alcun valore restituito. Ho dimenticato di menzionare che devo chiamare loadNibNamed nell'inizializzazione di una UIView.
Bagusflyer,

Cosa intendi con "nessun valore di ritorno"? selfè implicitamente restituito da tutti i initmetodi ...
Grimxn,

1
Quello che voglio dire è che chiamo loadNibNamed all'interno del metodo init. l'UIView caricato viene assegnato a self in ObjC. Ma in fretta, non lo è.
Bagusflyer,

5

Un buon modo per farlo con Swift è usare un enum.

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

Quindi nel tuo codice puoi semplicemente usare:

let view = Views.view1.getView()

2
Si noti che se lo si fa con un file pennino vuoto o un file pennino con un nodo radice UIView nessuno si arresta in modo anomalo poiché non si sta verificando l'integrità della dimensione dell'array o l'elemento in posizione 0.
Matthew Cawley

4

Preferisco questa soluzione (in base alla risposta se @ GK100):

  1. Ho creato un XIB e una classe denominata SomeView (usato lo stesso nome per praticità e leggibilità). Ho basato entrambi su una UIView.
  2. In XIB, ho modificato la classe "File's Owner" in SomeView (nella finestra di ispezione identità).
  3. Ho creato un punto vendita UIView in SomeView.swift, collegandolo alla vista di livello superiore nel file XIB (per comodità è stato chiamato "view"). Ho quindi aggiunto altri punti vendita ad altri controlli nel file XIB secondo necessità.
  4. In SomeView.swift, ho caricato XIB all'interno dell'inizializzatore inito init:frame: CGRect. Non è necessario assegnare nulla a "sé". Non appena viene caricato XIB, tutte le prese vengono collegate, inclusa la vista di livello superiore. L'unica cosa che manca è aggiungere la vista dall'alto alla gerarchia della vista:

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

preferisco usare init con frame, quindi ho sradicato questo! una cosa da notare ... aggiungi self.view.frame = frame se desideri che la vista corrisponda al frame in cui passi
Mike E

3

Swift 3 versione della risposta di 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

Ecco un modo chiaro e dichiarativo di caricare a livello di programmazione una vista usando un protocollo e un'estensione di protocollo (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
    }
}

E puoi usarlo così:

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

Alcune considerazioni aggiuntive :

  1. Assicurati che il file xib della tua vista personalizzata abbia il Custom Classset della vista (e punti vendita / azioni impostati da lì), non il proprietario del file.
  2. È possibile utilizzare questo protocollo / estensione all'esterno della vista personalizzata o interna. Potresti voler usarlo internamente se hai qualche altro lavoro di configurazione durante l'inizializzazione della vista.
  3. La classe di visualizzazione personalizzata e il file xib devono avere lo stesso nome.

2

Faccio solo così:

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

In questo esempio viene utilizzata la prima vista nel pennino "MyView.xib" nel pacchetto principale. Ma puoi variare l'indice, il nome del pennino o il pacchetto (principale per impostazione predefinita).

Ero abituato a visualizzare le viste nel metodo init view o a creare metodi generici come nelle soluzioni sopra (che sono intelligenti comunque), ma non lo faccio più.

In questo modo posso usare layout o tratti diversi mantenendo la stessa logica e codice di visualizzazione.

Trovo più facile lasciare che un oggetto factory (di solito il viewController che utilizzerà la vista) lo crei quando ne ha bisogno. A volte hai bisogno di un proprietario (di solito quando la vista creata ha uno sbocco collegato al creatore), a volte no ..

Questo è probabilmente il motivo per cui Apple non ha incluso un initFromNib metodo nella sua classe UIView ...

Per fare un esempio a livello del suolo, non sai come sei nato. Sei appena nato. Così sono le opinioni;)


2

Tutto quello che devi fare è chiamare il metodo init nel tuo UIView classe.

Fallo in questo modo:

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

Ora, se si desidera aggiungere questa vista come vista secondaria nel controller vista, farlo in questo modo nel file controller.swift vista:

self.view.addSubview(className())

è un'ottima risposta ma c'è qualcosa che non va, lo modificherò.
C0mrade,

È il modo in cui ho implementato. Ma puoi improvvisarlo. Grazie in anticipo @ C0mrade
Alap Anerao,

1

Simile ad alcune delle risposte sopra ma un'estensione UIView Swift3 più coerente:

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

Ciò offre la comodità di poter caricare la classe da un pennino con nome autonomo ma anche da altri pennini / fasci.


1

Puoi farlo tramite storyboard, basta aggiungere i vincoli adeguati per la visualizzazione. Puoi farlo facilmente subclassando qualsiasi vista dalla tua diciamo 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

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

Fornisco 2 varianti su come aggiungere vincoli - uno comune e all'interno del linguaggio del formato visivo - selezionare quello che si desidera :)

Inoltre, per impostazione predefinita si presuppone che il xibnome abbia lo stesso nome del nome della classe di implementazione. Se no, cambia e bastaxibName parametro.

Se si esegue la sottoclasse della vista da BaseView- è possibile inserire facilmente qualsiasi vista e specificare la classe in 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

Versione più potente basata sulla risposta di 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
    }
}

E puoi usare come

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

L'implementazione più conveniente. Qui hai bisogno di due metodi, per tornare direttamente all'oggetto della tua classe, non UIView.

  1. viewId contrassegnato come una classe , che consente l'override
  2. Il tuo .xib può contenere più di una vista del livello superiore, anche questa situazione viene gestita correttamente.

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

Esempio:

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

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

0

Se si desidera che la sottoclasse Swift UIView sia completamente autonoma e si abbia la possibilità di essere istanziati usando init o init (frame :) senza esporre i dettagli dell'implementazione dell'uso di un pennino, è possibile utilizzare un'estensione di protocollo per raggiungere questo obiettivo. Questa soluzione evita la gerarchia di UIView nidificata come suggerito da molte altre soluzioni.

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)

Le risposte solo al codice sono scoraggiate. Si prega di aggiungere alcune spiegazioni su come questo risolve il problema o su come differisce dalle risposte esistenti. Dalla recensione
Nick

0

Preferisco l'estensione qui sotto

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

La differenza tra questa e l'estensione con risposta migliore è che non è necessario memorizzarla come costante o variabile.

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

Quale versione di Xcode stai usando? Assicurati di utilizzare l'ultima versione di XCode. Funziona bene per me con XCode 11.5 (ultima versione come alla data).
iCoder

0
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.