Come caricare un UIView utilizzando un file pennino creato con Interface Builder


241

Sto cercando di fare qualcosa di un po 'elaborato, ma qualcosa che dovrebbe essere possibile. Quindi ecco una sfida per tutti voi esperti là fuori (questo forum è un branco di molti di voi ragazzi :)).

Sto creando un "componente" del questionario, che voglio caricare su un NavigationContoller(mio QuestionManagerViewController). Il "componente" è un "vuoto" UIViewController, che può caricare viste diverse a seconda della domanda a cui è necessario rispondere.

Il modo in cui lo sto facendo è:

  1. Creare l'oggetto Question1View come UIViewsottoclasse, definendone alcuni IBOutlets.
  2. Crea (utilizzando Interface Builder) il Question1View.xib (QUI È DOVE È PROBABILE IL MIO PROBLEMA ). Ho impostato sia la UIViewControllere UIViewdi essere di classe Question1View.
  3. Collego le prese con il componente della vista (usando IB).
  4. Sostituisco il initWithNibmio QuestionManagerViewControllerper apparire così:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
            // Custom initialization
        }
        return self;
    }

Quando eseguo il codice, visualizzo questo errore:

14/05/2009 15: 05: 37.152 iMobiDines [17148: 20b] *** Terminazione dell'app a causa di un'eccezione non rilevata ' NSInternalInconsistencyException', motivo: '-[UIViewController _loadViewFromNibNamed:bundle:] caricato il pennino "Question1View" ma l'output della vista non è stato impostato. "

Sono sicuro che esiste un modo per caricare la vista utilizzando il file pennino, senza la necessità di creare una classe viewController.

Risposte:


224

C'è anche un modo più semplice per accedere alla vista invece di gestire il pennino come un array.

1 ) Creare una sottoclasse di visualizzazione personalizzata con qualsiasi punto vendita a cui si desidera accedere in un secondo momento. --La ​​mia opinione

2 ) in UIViewController che si desidera caricare e gestire il pennino, creare una proprietà IBOutlet che conterrà la vista del pennino caricato, ad esempio

in MyViewController (una sottoclasse UIViewController)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(non dimenticare di sintetizzarlo e rilasciarlo nel tuo file .m)

3) apri il pennino (lo chiameremo 'myViewNib.xib') in IB, imposta il proprietario del tuo file su MyViewController

4) ora collega l'output del proprietario del file myViewFromNib alla vista principale nel pennino.

5) Ora in MyViewController, scrivi la seguente riga:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

Ora, non appena lo fai, chiamando la tua proprietà "self.myViewFromNib" avrai accesso alla vista dal tuo pennino!


4
Funzionerà per la vista del controller principale (self.view)? Per alcuni motivi devo caricare la vista principale dal pennino dopo il metodo di inizializzazione del controller standard. Motivo - struttura di sottoclasse complessa
Lukasz,

@Lukasz Quanto riesci a farlo? Anche io sto cercando qualcosa come te.
Ajay Sharma,

Mi dispiace che potrei essere spessa, ma sono confuso al primo punto. Come posso creare punti vendita in una sottoclasse della vista personalizzata? Pensavo che i punti vendita fossero solo per UIViewController?
jowie,

1
Che dire di un caso in cui questa vista appare su più viste principali? Quindi proponi di modificare manualmente xib per ognuno di essi? È TROPPO MALE !!!
user2083364

2
Funziona solo se si desidera utilizzare il pennino personalizzato in un controller a vista singola. Nel caso in cui si desideri utilizzarlo su diversi controller di visualizzazione, ognuno creando dinamicamente un'istanza del pennino personalizzato, ciò non funzionerà.
giovedì

120

Grazie a tutti. Ho trovato il modo di fare quello che volevo.

  1. Crea il tuo UIViewcon le IBOutlets di cui hai bisogno.
  2. Crea lo xib in IB, progettalo a tuo piacimento e collegalo in questo modo: Il proprietario del file è di classe UIViewController(nessuna sottoclasse personalizzata, ma quella "reale"). La vista del proprietario del file è collegata alla vista principale e la sua classe è dichiarata come quella dal passaggio 1).
  3. Collega i tuoi controlli con la IBOutlets.
  4. Il DynamicViewControllergrado di eseguire la sua logica di decidere cosa visualizzare / XI ter del carico. Una volta che ha preso la decisione, nel loadViewmetodo metti qualcosa del genere:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;

Questo è tutto!

Il loadNibNamedmetodo del bundle principale si occuperà di inizializzare la vista e creare le connessioni.

Ora ViewController può visualizzare una vista o un'altra a seconda dei dati in memoria e la schermata "padre" non deve preoccuparsi di questa logica.


2
Ho un problema molto simile: voglio semplicemente caricare un UIView da un file xib. Questi passaggi non funzionano per me: l'app si arresta in modo anomalo con "accesso errato" subito dopo aver aggiunto la vista caricata come sottoview.
Justicle,

2
Per iPhone 3.0, usa objectAtIndex: 0 per ottenere il primo elemento. Questo si blocca esattamente come descritto da Justicle. Qualche idea sul perché?
martedì

1
+1 ha funzionato perfettamente per me. Volevo aggiungere più viste che avevano il controller a una vista (sto usando uno scenario di capovolgimento in cui gira una sezione della vista) Ho dovuto fare l'indice sull'array 0 (iPhone 3.0)
Aran Mulholland

42
Questa è la risposta corretta, tuttavia, il codice dovrebbe essere modificato in modo che si giri attraverso nibViews e faccia un controllo di classe su ogni oggetto, in questo modo si ottiene sicuramente l'oggetto corretto. objectAtIndex: 1 è un presupposto pericoloso.
M. Ryan,

2
Jasconius ha ragione. Dovresti passare in rassegna l'array nibViews in questo modo: gist.github.com/769539
leviathan

71

Non sono sicuro di cosa stiano parlando alcune delle risposte, ma devo inserire questa risposta qui quando cercherò su Google la prossima volta. Parole chiave: "Come caricare un UIView da un pennino" o "Come caricare un UIView da un NSBundle."

Ecco il codice quasi al 100% direttamente dal libro Apress Beginning per iPhone 3 (pagina 247, "Utilizzo della nuova cella di visualizzazione tabella"):

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

Questo suppone che tu abbia una UIViewsottoclasse chiamata Blah, un pennino chiamato Blahche contiene un UIViewche ha la sua classe impostata su Blah.


Categoria: NSObject + LoadFromNib

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

Estensione rapida

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

E un esempio in uso:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}

2
Se lo usi isKindOfsei protetto dalle modifiche alla struttura del pacchetto.
Dan Rosenstark,

1
Una assertsarebbe probabilmente una soluzione migliore. In questo modo nasconderai solo il problema.
Sulthan,

1
@Sulthan un'asserzione è intrinsecamente diversa. Voglio l'oggetto di tipo Blah ovunque si trovi nel pennino . Non mi importa se è stato promosso o retrocesso. Tuttavia, in risposta al tuo commento Ho aggiunto un'asserzione, ma è non è al posto del forciclo. Lo completa.
Dan Rosenstark,

Puoi eseguire loadFromNib senza parametri: + (id) loadFromNib {NSArray * bundle = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass ([self class]) proprietario: self options: nil]; for (id id in bundle) {if ([object isKindOfClass: [self class]]) {oggetto return; }} restituisce zero; }
JVC,

22

Per tutti coloro che devono gestire più di un'istanza della vista personalizzata, ovvero una collezione outlet , ho unito e personalizzato le risposte @Gonso, @AVeryDev e @Olie in questo modo:

  1. Crea una personalizzazione MyView : UIViewe impostala come " Classe personalizzata " della radice UIViewnella XIB desiderata ; classe personalizzata

  2. Crea tutti i punti vendita di cui hai bisogno MyView(fallo ora perché dopo il punto 3 l'IB ti proporrà di collegare i punti vendita alla UIViewControllervista personalizzata e non a quella desiderata); outlet di classe personalizzata

  3. Imposta UIViewControllercome " Proprietario del file " della vista personalizzata XIB ; inserisci qui la descrizione dell'immagine

  4. Nel UIViewControlleraggiungi un nuovo UIViewsper ogni istanza MyViewdesiderata e collegali alla UIViewControllercreazione di una collezione outlet : queste viste fungeranno da viste "wrapper" per le istanze di visualizzazione personalizzate; inserisci qui la descrizione dell'immagine

  5. Infine, nel viewDidLoadtuo UIViewControlleraggiungi le seguenti righe:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..

Ciao ... sai come fare tutto programmaticamente, ad es. non usando affatto il file xib?
X-Coder

19

Vorrei utilizzare UINib per creare un'istanza di una UIView personalizzata da riutilizzare

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

I file necessari in questo caso sono MyCustomView.xib , MyCustomViewClass.h e MyCustomViewClass.m Si noti che [UINib instantiateWithOwner]restituisce un array, quindi è necessario utilizzare l'elemento che riflette l'UIView che si desidera riutilizzare. In questo caso è il primo elemento.


1
Non intendi "customNib" nella riga 2 invece di "rankingNib"
Danny il

11

Non dovresti impostare la classe del tuo controller di visualizzazione su una sottoclasse di UIViewInterface Builder. Questo è sicuramente almeno una parte del tuo problema. Lascialo come una UIViewControllersottoclasse o un'altra classe personalizzata che possiedi.

Per quanto riguarda il caricamento di solo una vista da un XI ter, ero sotto l'ipotesi che si doveva avere una sorta di controller della vista (anche se non si estende UIViewController, che può essere troppo pesante per le vostre esigenze) Imposta come proprietario del file in Interface Builder se vuoi usarlo per definire la tua interfaccia. Ho fatto una piccola ricerca per confermare anche questo. Questo perché altrimenti non ci sarebbe modo di accedere a nessuno degli elementi dell'interfaccia nel UIView, né ci sarebbe un modo per far attivare i propri metodi nel codice dagli eventi.

Se usi a UIViewController come proprietario del tuo file per le tue viste, puoi semplicemente usarlo initWithNibName:bundle:per caricarlo e riavere l'oggetto del controller di vista. In IB, assicurati di impostare la viewpresa sulla vista con la tua interfaccia in xib. Se usi un altro tipo di oggetto come proprietario del tuo file, dovrai usare NSBundleil loadNibNamed:owner:options:metodo per caricare il pennino, passando un'istanza del proprietario del file al metodo. Tutte le sue proprietà verranno impostate correttamente in base alle prese definite in IB.


Sto leggendo il libro di Apress, "Iniziare lo sviluppo di iPhone: esplorare l'SDK di iPhone" e hanno discusso di questo metodo nel capitolo 9: Controller di navigazione e Visualizzazioni tabella. Non è sembrato troppo complicato.
Lyndsey Ferguson,

Sarei curioso di vedere come si dice sia fatto. Non ho una copia di quel libro al momento.
Marc W,

10

Puoi anche usare initWithNibName di UIViewController invece di loadNibNamed. È più semplice, lo trovo.

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

Ora devi solo creare MySubView.xib e MySubView.h / m. In MySubView.xib imposta la classe Proprietario del file su UIViewController e visualizza la classe su MySubView.

È possibile posizionare e dimensioni del subviewfile usando il file xib padre.


8

Questa è una grande domanda (+1) e le risposte sono state quasi utili;) Mi dispiace ragazzi, ma ho avuto un sacco di tempo per frugare in questo, sebbene sia Gonso che AVeryDev abbiano dato buoni suggerimenti. Speriamo che questa risposta aiuti gli altri.

MyVC è il controller di visualizzazione che tiene tutte queste cose.

MySubview è la vista che vogliamo caricare da un xib

  • In MyVC.xib, crea una vista di tipo MySubViewche abbia la giusta dimensione e forma e sia posizionata nel punto desiderato.
  • In MyVC.h, avere

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
  • In MyVC.m, @synthesize mySubView;e non dimenticare di rilasciarlo dealloc.

  • In MySubview.h, avere un outlet / proprietà per UIView *view(potrebbe non essere necessario, ma ha funzionato per me.) Sintetizzare e rilasciarlo in .m
  • In MySubview.xib
    • impostare il tipo di proprietario del file su MySubviewe collegare la proprietà della vista alla vista.
    • Disporre tutti i bit e collegarli a IBOutletquelli desiderati
  • Di nuovo in MyVC.m

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];

La parte difficile per me è stata: i suggerimenti nelle altre risposte hanno caricato la mia vista dallo xib, ma NON hanno sostituito la vista in MyVC (duh!) - Ho dovuto scambiarlo da solo.

Inoltre, per accedere ai mySubviewmetodi di, ilview proprietà nel file .xib deve essere impostata su MySubview. Altrimenti, ritorna come un semplice vecchio UIView.

Se c'è un modo per caricare mySubviewdirettamente dal proprio xib, sarebbe rock, ma questo mi portò dove dovevo essere.


1
Ho usato questo metodo, grazie. Una modifica che ho apportato per consentirgli di supportare le visualizzazioni nidificate è stata la modifica [ self.view insertSubview: msView aboveSubview: mySubview]; a [ mySubview.superview insertSubview: msView aboveSubview: mySubview];
Jason,

8

Questo dovrebbe essere più facile. Ho finito per estendere UIViewControllere aggiungere un loadNib:inPlaceholder:selettore. Adesso posso dire

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

Ecco il codice per la categoria (fa lo stesso rigamarole come descritto da Gonso):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end

7

In fretta

In realtà la mia risoluzione a questo problema è stata quella di caricare la vista in viewDidLoad nel mio CustonViewController dove volevo usare la vista in questo modo:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

Non caricare la vista in un loadView()metodo! Il metodo loadView serve per caricare la vista per ViewController personalizzato.


6

Anch'io volevo fare qualcosa di simile, questo è quello che ho trovato: (SDK 3.1.3)

Ho un controller di visualizzazione A (a sua volta di proprietà di un controller Nav) che carica VC B alla pressione di un pulsante:

In AViewController.m

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

Ora VC B ha la sua interfaccia da Bnib, ma quando viene premuto un pulsante, voglio passare a una "modalità di modifica" che ha un'interfaccia utente separata da un pennino diverso, ma non voglio un nuovo VC per la modalità di modifica, Voglio che il nuovo pennino sia associato al mio VC esistente.

Quindi, in BViewController.m (nel metodo premere il pulsante)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

Quindi su un altro pulsante premere (per uscire dalla modalità di modifica):

[editView removeFromSuperview];

e sono tornato al mio Bnib originale.

Funziona bene, ma nota il mio EditMode.nib ha solo 1 oggetto di livello superiore, un oggetto UIView. Non importa se il proprietario del file in questo pennino è impostato come BViewController o l'oggetto predefinito NSO, ma assicurarsi che View Outlet nel proprietario del file NON sia impostato su nulla. Se lo è, allora ottengo un crash exc_bad_access e xcode procede a caricare 6677 frame di stack che mostrano un metodo UIView interno chiamato ripetutamente ... quindi sembra un ciclo infinito. (Tuttavia, il View Outlet è impostato nel mio Bnib originale)

Spero che questo ti aiuti.


Leggendo tra le righe, anche questo mi ha aiutato.
petert

In base alle informazioni sul link non è necessario manipolare i controller di visualizzazione in questo modo.
T. Markle,

6

Ho creato una categoria che mi piace:

UIView+NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

Quindi, chiama in questo modo:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

Utilizzare un nome pennino se il pennino ha un nome diverso dal nome della classe.

Per sovrascriverlo nelle sottoclassi per un comportamento aggiuntivo, potrebbe essere simile al seguente:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}

5

Per utenti Swift con opzione progettabile:

  1. Crea una UIViewsottoclasse personalizzata e un file xib , che chiameremo dopo il nostro nome di classe: nel nostro caso MemeView. All'interno della classe Meme View ricordati di definirlo come designabile con l' @IBDesignableattributo prima della dichiarazione della classe
  2. Ricorda di impostare il proprietario del file in xib con la nostra UIViewsottoclasse personalizzata nel pannello Ispettore Indetità

    inserisci qui la descrizione dell'immagine

  3. Nel file xib ora possiamo costruire la nostra interfaccia, creare vincoli, creare sbocchi, azioni ecc. inserisci qui la descrizione dell'immagine

  4. Dobbiamo implementare alcuni metodi per la nostra classe personalizzata per aprire xib una volta inizializzato

    class XibbedView: UIView {

    weak var nibView: UIView!
    
    override convenience init(frame: CGRect) {
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        self.init(nibName: nibName)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    init(nibName: String) {
        super.init(frame: CGRectZero)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    func setUpConstraints() {
        ["V","H"].forEach { (quote) -> () in
            let format = String(format:"\(quote):|[nibView]|")
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
        }
    }
    
    func loadNib(name: String) -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: name, bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
    
        return view
    }

    }

  5. Nella nostra classe personalizzata possiamo anche definire alcune proprietà ispezionabili per avere il pieno controllo su di esse dal builder di interfacce

    @IBDesignable class MemeView: XibbedView {

    @IBInspectable var memeImage: UIImage = UIImage() {
        didSet {
            imageView.image = memeImage
        }
    }
    @IBInspectable var textColor: UIColor = UIColor.whiteColor() {
        didSet {
            label.textColor = textColor
        }
    }
    @IBInspectable var text: String = "" {
        didSet {
            label.text = text
        }
    }
    @IBInspectable var roundedCorners: Bool = false {
        didSet {
            if roundedCorners {
                layer.cornerRadius = 20.0
                clipsToBounds = true
            }
            else {
                layer.cornerRadius = 0.0
                clipsToBounds = false
            }
        }
    }
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!

}

Alcuni esempi:
inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Se abbiamo bisogno di aggiungere ulteriori informazioni la vista mentre è visualizzata all'interno di uno storyboard o di un altro xib, per fare ciò che possiamo implementare prepareForInterfaceBuilder(), questo metodo verrà eseguito solo durante l'apertura del file nel builder di interfacce. Se hai fatto tutto ciò che ho scritto ma non funziona nulla, è un modo per eseguire il debug di una vista sigle aggiungendo punti di interruzione nella sua implementazione. Ecco la gerarchia delle viste. inserisci qui la descrizione dell'immagine
Visualizza hiearchy

Spero che questo aiuti un campione completo può essere scaricato qui


L'articolo completo può essere consultato dal mio blog cloudintouch.it/2016/04/21/designable-xib-in-xcode-hell-yeah ma ho già pubblicato la parte pertinente.
Andrea,

let nib = loadNib(nibName)questa è un'istanza di XibbedView, se chiami `` addSubview (pennino) `` `` allora stai aggiungendo un'istanza di XibbedView a un'altra istanza di XibbedView?
William Hu,

sono d'accordo su questo punto. Quando provi a vederlo da "Debug View Hierarchy" sembra che XibbedView contenga un XibbedView. Puoi vederlo.
William Hu,

@WilliamHu se carichi l'esempio fornito puoi vedere che MemeView.xib è solo un'istanza di UIView con una serie di sottoview, il titolare del file è un MemeView che è una sottoclasse di XibbedView. Quindi l'unica istanza di XibbedView è solo MemeView che carica un file xib in cui la vista principale è solo una vista
Andrea

Oh ok, allora. Lo proverò. Grazie!
William Hu,

4

Ho trovato questo post sul blog di Aaron Hillegass (autore, istruttore, ninja del cacao) molto illuminante. Anche se non adotti il ​​suo approccio modificato al caricamento di file NIB tramite un inizializzatore designato, probabilmente otterrai almeno una migliore comprensione del processo in corso. Ho usato questo metodo ultimamente con grande successo!


Il link è morto. Credo che ora si trovi su bignerdranch.com/blog/…
joelliusp il

3

La risposta precedente non tiene conto di un cambiamento nella struttura NIB (XIB) tra 2.0 e 2.1 dell'SDK per iPhone. I contenuti dell'utente ora iniziano con l'indice 0 anziché 1.

È possibile utilizzare la macro 2.1 valida per tutte le versioni 2.1 e successive (ovvero due trattini bassi prima di IPHONE:

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

Usiamo una tecnica simile a questa per la maggior parte delle nostre applicazioni.

Barney


2
Barney: "La risposta precedente" non ha senso su Stack Overflow perché le risposte cambiano posizione in base al voto. Meglio fare riferimento alla "risposta di Fred" o alla "risposta di Barney" o alla "risposta di Olie".
Olie,

3

Avevo motivo di fare la stessa cosa (caricando a livello di codice una vista da un file XIB), ma dovevo farlo interamente dal contesto di una sottoclasse di una sottoclasse di a UIView(cioè senza coinvolgere il controller di vista in alcun modo). Per fare ciò ho creato questo metodo di utilità:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

Quindi lo chiamo dal initWithFramemetodo della mia sottoclasse in questo modo:

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

Inserito per interesse generale; se qualcuno vede qualche problema senza farlo in questo modo, per favore fatemelo sapere.


Ho implementato il tuo codice, ma continua a non rientrare nell'ambito di init. Ho creato il tuo metodo statico in una classe di utilità. Ho creato file h / m chiamati MyView (sottoclasse da UIView) e un xib chiamato lo stesso. In IB ho impostato il proprietario dei file xib E la vista su "MyView". Nel debugger è fuori portata. Mi sto perdendo qualcosa in IB?
TigerCoding

Si prega di dare un'occhiata al mio nuova domanda ecco: stackoverflow.com/questions/9224857/...
TigerCoding

self = [Utilità initWithNibName: @ "XIB1" withSelf: self]; Stai passando te stesso quando non è ancora impostato. Il codice non è lo stesso di questo: self = [Utilities initWithNibName: @ "XIB1" withSelf: nil]; ?
Ken Van Hoeylandt,

@Javy: il mio esempio di codice qui potrebbe non funzionare con ARC - Non l'ho testato con ARC abilitato.
MusiGenesis,

3

Ecco un modo per farlo in Swift (attualmente scrivendo Swift 2.0 in XCode 7 beta 5).

Dalla tua UIViewsottoclasse impostata come "Classe personalizzata" in Interface Builder crea un metodo come questo (la mia sottoclasse si chiama RecordingFooterView):

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

Quindi puoi semplicemente chiamarlo così:

let recordingFooterView = RecordingFooterView.loadFromNib()


2

Nessuna delle risposte spiega come creare lo XIB autonomo che è la radice di questa domanda. Non esiste alcuna Xcode 4opzione per "Crea nuovo file XIB".

Per fare questo

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

Questo può sembrare semplice, ma può farti risparmiare qualche minuto frugando perché la parola "XIB" non appare da nessuna parte.


1

@AVeryDev

6) Per allegare la vista caricata alla vista del controller della vista:

[self.view addSubview:myViewFromNib];

Presumibilmente, è necessario rimuoverlo dalla vista per evitare perdite di memoria.

Per chiarire: il controller di visualizzazione ha diversi IBOutlet, alcuni dei quali sono collegati agli elementi nel file pennino originale (come al solito) e alcuni sono collegati agli elementi nel pennino caricato. Entrambi i pennini hanno la stessa classe proprietario. La vista caricata si sovrappone a quella originale.

Suggerimento: imposta a zero l'opacità della vista principale nel pennino caricato, quindi non oscurerà gli oggetti dal pennino originale.


buona cosa a cui fare attenzione, ma l'aggiunta di una vista come sottoview la rimuove automaticamente dal suo genitore precedente, quindi non dovrebbe perdere.
averydev,

1

Dopo aver trascorso molte ore, ho forgiato la seguente soluzione. Seguire questi passaggi per creare personalizzato UIView.

1) Creare la classe myCustomView ereditata da UIView .
inserisci qui la descrizione dell'immagine

2) Crea .xib con il nome myCustomView .
inserisci qui la descrizione dell'immagine

3) Cambia la classe di UIView nel tuo file .xib, assegna lì la mia classe myCustomView .
inserisci qui la descrizione dell'immagine

4) Creare IBOutlet
inserisci qui la descrizione dell'immagine

5) Carica .xib in myCustomView * customView . Utilizzare il seguente codice di esempio.

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

Nota: per coloro che ancora affrontano il problema possono commentare, fornirò loro il link del progetto di esempio con myCustomView


1

Versione più corta:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

0

Ho una convenzione di denominare xibs con viste uguali a quelle della vista. Come si farebbe per un controller di visualizzazione. Quindi, non devo scrivere i nomi delle classi nel codice. Carico un UIView da un file pennino con lo stesso nome.

Esempio per una classe chiamata MyView.

  • Creare un file pennino chiamato MyView.xib in Interface Builder
  • In Interface Builder, aggiungi un UIView. Imposta la sua classe su MyView. Personalizza il contenuto del tuo cuore, collega le variabili di istanza di MyView alle visualizzazioni secondarie a cui potresti voler accedere in un secondo momento.
  • Nel tuo codice, crea un nuovo MyView in questo modo:

    MyView * myView = [MyView nib_viewFromNibWithOwner: owner];

Ecco la categoria per questo:

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

Quindi collegherei i pulsanti con le azioni del controller che sto usando e imposterei le cose sulle etichette usando le prese nella mia sottoclasse della vista personalizzata.


0

Per caricare a livello di programmazione una vista da un pennino / xib in Swift 4:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}

-1

Ho finito per aggiungere una categoria a UIView per questo:

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

spiegazione qui: viewcontroller riduce il caricamento della vista in iOS & Mac


Hrm. Che cos'è il tag?
n

per sapere quale oggetto di livello superiore conservare. al momento era necessario, ma probabilmente è possibile eliminare i ritardi per conformarsi ad ARC ora.
Gngrwzrd,
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.