IBOutlet è nullo, ma è collegato nello storyboard, Swift


102

Utilizzo di Swift 1.1 e Xcode 6.2.

Ho una UIStoryboard contenente una singola UIViewControllersottoclasse personalizzata . Su di esso, ho una @IBOutletconnessione di tipo UIViewda quel controller a una UIView sottoclasse nello storyboard. Ho anche punti vendita simili per le visualizzazioni secondarie di quella vista. Vedere la figura A.

Ma in fase di esecuzione, queste proprietà sono nulle (Figura B). Anche se ho assicurato di aver collegato le prese in Interface Builder.

Pensieri :

  • È possibile che, poiché sto usando una sottoclasse di una sottoclasse, qualcosa vada a male con l'inizializzazione? Non sovrascrivo alcun inizializzatore
  • awakeFromNib: non viene chiamato per qualche motivo
  • Forse non si connette alle visualizzazioni secondarie nelle visualizzazioni secondarie

Cose che ho provato:

  • @IBOutletTipi di elementi corrispondenti e storyboard esattamente (invece di UIView)
  • Eliminare proprietà e outlet e aggiungerli nuovamente

Figura A

Figura A *

Figura B

Figura B

* Il codice oscurato in Figure Aè:

@IBOutlet private var annotationOptionsView: UIView!
@IBOutlet private var arrivingLeavingSwitch: UISegmentedControl!

Grazie.


Perché non cambiare il! per ?

clearView è nullo perché non è collegato allo storyboard (vedi il cerchio a sinistra del codice con un buco, che indica che non è linkato), nello screenshot non vedo la dichiarazione di annotationOptionView.
Javier Flores Font

@ JavierFloresFont: clearViewmi aspetto di essere nullo. È qualcosa che devo ancora refactoring. Vedi anche la nota a piè di pagina per la figura A. @ShaanSingh Dovrebbe essere! perché le connessioni dagli storyboard sono (dovrebbero essere) impostate in fase di esecuzione e non dovrebbero essere nulle.
Stefan Arambasich

Come viene caricato questo controller di visualizzazione? Mostraci il codice che lo richiede o descrivi il segue che si collega ad esso.
rob mayoff

1
Sta ottenendo lo storyboard giusto: let vc = UIStoryboard(name: "LocationPickerModal", bundle: nil) .instantiateViewControllerWithIdentifier("LocationPickerModalViewController") as LocationPickerModalViewController
Stefan Arambasich

Risposte:


146

In genere ciò accade perché il controller di visualizzazione non ha ancora caricato la sua gerarchia di visualizzazione. Un controller di visualizzazione carica la sua gerarchia di visualizzazione solo quando qualcosa gli invia il viewmessaggio. Il sistema lo fa quando è il momento di mettere effettivamente la gerarchia di visualizzazione sullo schermo, il che accade dopo che cose come prepareForSegue:sender:e viewWillAppear:sono tornate.

Poiché il tuo VC non ha ancora caricato la sua gerarchia di visualizzazione, i tuoi punti vendita sono ancora nulli.

Potresti forzare il VC a caricare la sua gerarchia di visualizzazione dicendo _ = self.view.


4
Questo accade quando viewWillAppear:viene chiamato, la prima volta che faccio qualcosa con quella presa. L'accesso alla visualizzazione non fa nulla per me. Ancora zero.
Stefan Arambasich

Grazie! Questo ha funzionato anche per me. var v = viewcontroller.view; costretto a caricare la gerarchia di visualizzazione. Bel piccolo trucco! ;)
Yordi Uytersprot il

@YordiUytersprot dove metteresti questa frase?
Async

4
Bene, se installi un viewcontroller: let vc = self.storyboard? .InstantianteViewControllerWithIdentifier ("StoryboardIDfromVC") as? CustomVC; let view = vc.view; // In questo momento, puoi accedere a IBOutlet da CustomVC qui .. vc.customTextField.text = "Lalala"; Spero ti aiuti! :)
Yordi Uytersprot

4
UIViewcontroller.loadViewIfNeeded()è il metodo giusto da usare qui.
orkoden

27

Hai provato a eseguire Prodotto> Pulisci. Ha risolto un problema molto simile per me.


L'ho fatto. Scusa, volevo sottolineare la mia risposta. Ha iniziato a funzionare solo dopo aver eliminato la DerivedDatacartella per il progetto.
Stefan Arambasich

3
Solo due dei miei punti vendita nel mezzo della mia gerarchia di visualizzazione erano nulli .. Prodotto -> Clean ha funzionato per me!
Rikco

1
obj c, solo la pulizia del progetto ha funzionato per me ... non posso credere che sia passato più di un'ora e non ho nemmeno pensato di pulire il progetto: / ma grazie !!
serra

Solo prodotto -> Clean non ha funzionato per me, ma ho avviato un simulatore diverso e ha funzionato, quindi deve esserci qualcosa di sbagliato anche nella cartella DerivedData.
endavid

22

Hai istanziato il tuo controller di visualizzazione da uno Storyboard o NIB o lo hai istanziato direttamente tramite un inizializzatore?

Se hai istanziato la tua classe direttamente con l'inizializzatore, le prese non saranno collegate. Interface Builder crea istanze personalizzate delle tue classi e codifica tali istanze in NIB e Storyboard per la decodifica ripetuta, non definisce le classi stesse. Se questo era il tuo problema, devi solo modificare il codice in cui crei il controller per utilizzare invece i metodi su UIStoryboard o UINib.


2
Sto creando UIStoryboardun'istanza e invocandola instantiateViewControllerWithIdentifier.
Stefan Arambasich

15

Lo storyboard non riconosceva altre cose dell'interfaccia utente che ho aggiunto ad esso. In fase di esecuzione tutti i riferimenti erano nulli. Quindi ho cancellato la mia cartella dei dati derivati ​​e poi quelle connessioni hanno funzionato di nuovo.


2
Questa è la risposta. Ho combattuto questo per un bel po ', pulendo la cartella build e tutto il resto. Nel mio caso, in viewDidLoad()tutti i collegamenti sono stati effettuati, ma in viewWillAppear()quasi tutti lo erano nil(a volte uno o due erano ancora collegati). Ho provato di tutto e alla fine ho eliminato la cartella dei dati derivati, ricostruita e tutto è andato bene.
ruttopia

6
Anche dopo 3 anni, Xcode 9.2 ha ancora lo stesso maledetto bug. Grazie mille.
Kemal Can Kaynak

1
Qui per segnalare che questo esiste ancora in Xcode 11. :(
spendag

13

Questo mi stava accadendo con la mia cella di visualizzazione della raccolta personalizzata. È venuto fuori che ho dovuto sostituire il mio metodo registerClassforReuseIdentifier con registerNib. Questo ha risolto il problema per me.


Hai ragione. Ho letto in dettaglio il contenuto del conteggio ARC in Swift Programming Language. Non riesco a trovare il motivo per cui la proprietà debole di IBOutlet è ancora nulla. Alla fine, cerco su Google "@IBOutlet swift nil" e trovo la tua risposta in SO. Quindi ho segnato la riga Xcode template aggiunto 'self.collectionView! .RegisterClass' in viewDidLoad. Quindi funziona. Mi salvi la vita.
charles.cc.hsu

12

Questo è successo per me perché stavo accidentalmente istanziando direttamente il mio controller della vista invece di istanziarlo attraverso lo storyboard. Se installi direttamente tramite, MyViewController()le prese non saranno collegate.


Se si utilizza il file XIB, l'istanza diretta funziona ancora correttamente.
Luat Vu Dinh

11

Nel mio caso, è successo perché ho sovrascritto il loadViewmetodo nella mia sottoclasse ViewController, ma ho dimenticato di aggiungere[super loadView]

-(void)loadView {
// blank
}

Quando sovrascrivi il loadViewmetodo, è tua responsabilità iniziare le tue visualizzazioni secondarie. Poiché lo sovrascrivi, le viste dal generatore di interfacce non hanno la possibilità di convertirsi in oggetti di cacao e quindi gli sbocchi rimangono nulli.

Se si implementa loadView nella sottoclasse del controller della vista, diventa tua responsabilità caricare gli elementi dell'interfaccia utente da storyboard / xib nel codice.

O semplicemente chiama

[super loadView];

In modo che la superclasse abbia la possibilità di caricare storyboard / xib nel codice.


8

Puoi chiamare controller.viewper forzare il caricamento della vista per inizializzare gli IBOutlet, quindi sarai in grado di assegnare i valori.

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if (segue.identifier == "identifier") {
        let controller = segue.destinationViewController as! YourController
        let _ = controller.view //force to load the view to initialize the IBOutlets

        controller.your_IBOutlet_property = xxx
        ...
        controller.delegate = self
    }
}

7

Se crei un'istanza view controllertramite a livello di codice. Quindi prova a crearlo come di seguito

let initialVC = self.storyboard?.instantiateViewController(withIdentifier: "InitialVC") as! InitialVC

invece che direttamente

let initialVC = InitialVC()

Questo ha funzionato per me.


6

Per me, questo si è verificato quando ho dichiarato accidentalmente la classe del mio controller di visualizzazione come

class XYZViewController: UINavigationController {
}

(cioè come UINavigationControllernon a UIViewController).

Xcode non raccoglie su questo errore, la classe sembra costruire bene, e le funzioni di override, come viewDidLoad, viewWillAppeare così via tutto il lavoro in modo corretto. Ma nessuno di IBOutletloro si connette.

Modifica della dichiarazione in

class XYZViewController: UIViewController {
}

risolto completamente.


5

Ho riscontrato questo problema di recente! Ecco il mio pensiero. Il problema non riguarda lo storyboard o qualsiasi problema di collegamento. Riguarda come si avvia il ViewController. Soprattutto quando utilizzi Swift (non c'è quasi nulla nell'editor quando crei un file di classe)

Usando semplicemente il init()from super class non puoi iniziare nulla con cui hai lavorato con story board. Quindi quello che devi fare è cambiare l'inizializzazione del ViewController. Sostituisci let XXViewController = XXViewController() con let XXViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("XXViewController") as! XXViewController Questo dice al programma di andare allo storyboard per trovare XXViewController e avvia tutto IBOutletnello storyboard.

Spero che questo aiuto ~ GL


Non era questo il problema
Stefan Arambasich

4

2019, UNA POSSIBILITÀ PER QUESTO ORRIBILE PROBLEMA:

  • Supponiamo che tu abbia forse una vista contenitore che mostra una sorta di orologio. Quindi l'hai fatto

    class Clock: UIViewController

In realtà lo usi in diversi posti nell'app .

Nella schermata principale, nella schermata dei dettagli, nella schermata di modifica.

Hai una complicata app moderna simile a Snapchat.

Infatti, Clockpuò effettivamente essere caricato più volte allo stesso tempo da qualche parte sulla stessa schermata . (Forse è nascosto in alcuni casi.)

Inizi a lavorare su un'istanza di Clockuno dei tuoi tanti storyboard.

Su quello storyboard aggiungi un'etichetta, NewLabel.

Naturalmente aggiungi la presa in codice. Tutto dovrebbe funzionare. Tutti gli altri punti vendita funzionano perfettamente.

Hai sicuramente collegato l'outlet.

Ma l'app si arresta in modo anomalo con NewLabel come zero.

Xcode ti dice chiaramente "ti sei dimenticato di collegare la presa".

Il motivo è questo .......... hai "NewLabel" solo su uno degli usi dello storyboard di Clock!

Il crash è in realtà da >>> un altro posto <<<< stai usando Clock !!!!

Xcode non ti dice che il crash proviene da un altro posto, non da dove stai lavorando!

L'incidente in realtà non proviene dal luogo in cui stai lavorando, ma da un altro storyboard, dove non c'è alcun elemento "NewLabel" su quello storyboard !!!

Frustrante.


3

Per Swift 3.

func configureView() {
    let _  = self.view
}

2

È necessario caricare prima la gerarchia della vista per creare un'istanza degli outlet nello storyboard. Per questo, puoi chiamare manualmente i metodi loadView o loadViewIfNeeded.


2

Nel mio caso, l'app ha iniziato a bloccarsi all'improvviso. Il debug ha rivelato che tutti i punti vendita erano ancora nilal momento di viewDidLoad().

La mia app utilizza ancora i pennini (non gli storyboard) per la maggior parte dei controller di visualizzazione. Tutto era a posto, tutte le prese cablate correttamente. Ho ricontrollato.

In genere istanziamo i nostri controller di visualizzazione come

let newVC = MYCustomViewController()

... che per qualche motivo sembra funzionare fino a quando la .xib è chiamato lo stesso della classe di visualizzazione del controller (non sono sicuro come funziona, però. Siamo Non chiediamo init(nibName:bundle:)con argomenti negative, o ignorando init()di farlo su selfdi simile è tipicamente suggerito ...).

Quindi ho provato a chiamare esplicitamente

 let newVC = MYCustomViewController(nibName: "MYCustomViewController", bundle: .main)

... solo per essere accolti con l'errore di eccezione di runtime:

*** Chiusura dell'app a causa di un'eccezione non rilevata "NSInternalInconsistencyException", motivo: "Impossibile caricare NIB nel bundle:" NSBundle </ Users / nicolasmiari / Library / Developer / CoreSimulator / Devices / 3DA3CF21-108D-498F-9649-C4FC9E3C1A8D / data /Containers/Bundle/Application/C543DDC1-AE86-4D29-988C-9CCE89E23543/MyApp.app> (caricato) 'con nome' MYCustomViewController ''

E poi , l'ho visto:

La casella di controllo "Appartenenza di destinazione" del file .xib era deselezionata.


Deve essersi verificato durante la risoluzione di uno dei frequenti conflitti di unione relativi al file di progetto Xcode.

Apple ha sicuramente bisogno di trovare un formato di file di progetto più compatibile con SCM.


2

Soluzione funzionante al 100% per la creazione di ViewController da XIB senza StoryBoard

  1. Crea la classe CustomViewController: UIViewController
  2. Crea visualizzazione CustomViewControllerView.xib
  3. In CustomViewControllerView.xib in Interface Builder selezionare Segnaposto -> Proprietario del file
  4. In "Ispettore attributi" imposta Class su CustomViewController
  5. In "Ispettore connessioni" connetti "vista" alla vista di primo livello di xib (assicurati che la classe della vista di primo livello non punti a CustomViewController)
  6. In "Ispettore connessioni" collegare altre prese (se necessario / esistono)
  7. Creare un'istanza di CustomViewController nel controller della vista padre / delegato dell'app

7.1.

// creating instance
let controller = CustomViewController()

7.2.

// connecting view/xib with controller instance
let bundle = Bundle(for: type(of: controller))
bundle.loadNibNamed("CustomViewControllerView", owner: controller, options: nil)

7.3.

// get/set outlets
controller.labelOutlet.text = "title"
controller.imageOutlet.image = UIImage(named: "image1")

Risposta fantastica, grazie! Per quello che vale, puoi anche sovrascrivere init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)e chiamare il passaggio 7.2 con selfdirettamente nella classe CustomViewController.
brandonscript

1

Controlla la tua connessione IBOutlet se è collegata al proprietario del file o alla vista. Potrebbero esserci degli errori.


1

Altro caso:

I tuoi punti vendita non verranno impostati finché la vista del controller di visualizzazione non viene effettivamente istanziata, cosa che nel tuo caso probabilmente sta accadendo poco dopo initWithNibName: bundle: —a quel punto saranno ancora nulle. Qualsiasi configurazione eseguita che coinvolga tali prese dovrebbe avvenire nel metodo -viewDidLoad del controller della vista.


1

Per me, ho avuto lo stesso errore su uno storyboard localizzato, un elemento è stato aggiunto in alcune impostazioni locali e non nell'altro, quindi avevo un riferimento nullo per quell'elemento quando sono passato alla locale dell'elemento mancante, ho dovuto rimuovere la localizzazione (ridondante) per quello storyboard utilizzando https://stackoverflow.com/a/42256341/1356559 .


1

Per me, questo è stato un arresto anomalo perché containerViewera nullo.

Ecco il mio codice con Crash .

@IBOutlet private var containerView: UIView!  // Connected to Storyboard
override open func loadView() {
    containerView.addSubview(anotherView)
}

La cosa che mancava era chiamare il super.loadView(). Quindi l'aggiunta ha risolto il problema per me.

Codice fisso :

@IBOutlet private var containerView: UIView!
override open func loadView() {
    super.loadView()
    containerView.addSubview(anotherView)
}

non dovresti mai chiamare direttamente loadView () ( developer.apple.com/documentation/uikit/uiviewcontroller/… ), chiama invece self.view
Lio

1

Ho avuto un problema simile quando avevo precedentemente aggiunto register (_: forCellReuseIdentifier :) per la cella personalizzata dopo aver già definito l'identificatore nello storyboard. Aveva questo codice nella funzione viewDidLoad (). Una volta rimosso, ha funzionato bene.


1

Ancora un altro caso in cui mi sono imbattuto. Ho cambiato il nome della mia classe per UIViewController, ma ho dimenticato di cambiare il nome del file .xib in cui è stata creata l'interfaccia.

Una volta rilevato questo e fatto che i nomi dei file riflettessero il nome della classe, è andato tutto bene!

Spero che aiuti qualcuno.


1

Ne ho uno in più ...

Se hai una classe personalizzata per un UITableViewCell ma dimentica di specificare Personalizzato nello stile della cella.


1

È possibile verificare se la visualizzazione è caricata.

if isViewLoaded && view.window != nil {
 //self.annotationOptionsView.
}

0
  1. selezionare entrambi .he .mvisualizzare i file del controller
  2. rimuovere il riferimento di quei file
  3. aggiungere nuovamente i file all'albero del progetto
  4. apri lo storyboard, eventualmente ricostruisci il progetto

0

Accidentalmente ho sottoclasse il mio controller di visualizzazione con AVPlayerViewControllerinvece di UIViewController. Ripetendo le UIViewControllercose alla normalità. Questo dovrebbe aiutare.

Nessuna pulizia della build (normale e completa), la rimozione delle cartelle dei dati derivati ​​e l'uscita da Xcode hanno funzionato per me.


0

Ho avuto lo stesso problema dopo aver copiato una classe (collegata a un xib) per riutilizzarla con un'altra classe di viewcontroller (collegata a uno storyboard). Ho dimenticato di rimuovere

override var nibName

e

override var nibBundle

metodi.

Dopo averli rimossi, i miei punti vendita hanno iniziato a funzionare.


0

Vedo che usi ViewController!? in ViewControllerclasse che si deve usare -viewDidLoad, non -awakeFromNib , -awakeFromNibutilizza per UIViewclasse



0

Se ne hai due main.storyboardse stai apportando modifiche a quella sbagliata, questo può accadere. Questo può accadere ogni volta che si collega una presa da un non istanziato storyboard.

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.