IBOutlet est nul, mais il est connecté dans le storyboard, Swift

102

Utilisation de Swift 1.1 et Xcode 6.2.

J'ai un UIStoryboard contenant une UIViewControllersous-classe personnalisée et singulière . Sur celui-ci, j'ai une @IBOutletconnexion de type UIViewde ce contrôleur à une UIView sous - classe sur le storyboard. J'ai également des points de vente similaires pour les sous-vues de cette vue. Voir la figure A.

Mais au moment de l'exécution, ces propriétés sont nulles (figure B). Même si j'ai assuré que j'ai connecté les prises dans Interface Builder.

Réflexions :

  • Est-il possible que, parce que j'utilise une sous-classe d'une sous-classe, quelque chose gâche l'initialisation? Je ne remplace aucun initialiseur
  • awakeFromNib: n'est pas appelé pour une raison quelconque
  • Peut-être qu'il ne se connecte pas aux sous-vues sur les sous-vues

Les choses que j'ai essayées:

  • Correspondance @IBOutletet types d'éléments de storyboard exactement (au lieu de UIView)
  • Supprimer la propriété et le point de vente et les ajouter de nouveau

Figure A

Figure A *

Figure B

Figure B

* Le code masqué dans Figure Aest:

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

Je vous remercie.

Stefan Arambasich
la source
Pourquoi ne pas changer le! à ?
clearView est nul car n'est pas lié au storyboard (voir le cercle à gauche du code avec un trou, qui indique qu'il n'est pas lié), dans la capture d'écran, je ne vois pas la déclaration d'annotationOptionView.
Javier Flores Font
@JavierFloresFont: clearViewJe m'attends à être nul. C'est quelque chose que je n'ai pas encore refactorisé. Voir également la note de bas de page de la figure A. @ShaanSingh Ça devrait être! car les connexions des storyboards sont (supposées être) définies lors de l'exécution et ne doivent pas être nulles.
Stefan Arambasich
Comment ce contrôleur de vue est-il chargé? Montrez-nous le code qui le demande, ou décrivez la séquence qui s'y connecte.
rob mayoff
1
Il obtient le bon storyboard: let vc = UIStoryboard(name: "LocationPickerModal", bundle: nil) .instantiateViewControllerWithIdentifier("LocationPickerModalViewController") as LocationPickerModalViewController
Stefan Arambasich

Réponses:

146

Cela se produit généralement parce que votre contrôleur de vue n'a pas encore chargé sa hiérarchie de vues. Un contrôleur de vue ne charge sa hiérarchie de vues que lorsque quelque chose lui envoie le viewmessage. Le système fait cela quand il est temps de mettre la hiérarchie des vues à l'écran, ce qui se produit après que des choses comme prepareForSegue:sender:et viewWillAppear:soient revenues.

Puisque votre VC n'a pas encore chargé sa hiérarchie de vues, vos points de vente sont toujours nuls.

Vous pouvez forcer le VC à charger sa hiérarchie de vues en disant _ = self.view.

Rob Mayoff
la source
4
Cela se produit lors de l' viewWillAppear:appel, la première fois que je fais quoi que ce soit avec cette prise. Accéder à la vue ne fait rien pour moi. Toujours nul.
Stefan Arambasich
Merci! Cela a fonctionné pour moi aussi. var v = viewcontroller.view; forcé de charger la hiérarchie des vues. Joli petit truc! ;)
Yordi Uytersprot
@YordiUytersprot où mettriez-vous cette ligne?
Async
4
Eh bien, si vous instanciez un viewcontroller: laissez vc = self.storyboard? .InstantianteViewControllerWithIdentifier ("StoryboardIDfromVC") as? CustomVC; laissez vue = vc.view; // Pour le moment, vous pouvez accéder aux IBOutlets à partir de CustomVC ici .. vc.customTextField.text = "Lalala"; J'espère que cela vous aide! :)
Yordi Uytersprot
4
UIViewcontroller.loadViewIfNeeded()est la bonne méthode à utiliser ici.
orkoden
27

Avez-vous essayé d'exécuter Produit> Nettoyer. Résolu un problème très similaire pour moi.

ThottChief
la source
J'ai fait. Désolé, destiné à marquer ma réponse. Il n'a commencé à fonctionner qu'après avoir supprimé le DerivedDatadossier du projet.
Stefan Arambasich
3
Seuls deux de mes points de vente au milieu de ma hiérarchie de vues étaient nuls. Produit -> Clean a fonctionné pour moi!
Rikco
1
obj c, le nettoyage du projet a fonctionné pour moi ... je ne peux pas croire que j'ai passé plus d'une heure et que je n'ai même pas pensé à nettoyer le projet: / mais merci !!
serre
Just Product -> Clean ne fonctionnait pas pour moi, mais j'ai démarré un autre simulateur et cela a fonctionné, il doit donc y avoir quelque chose qui ne va pas dans le dossier DerivedData.
endavid
22

Avez-vous instancié votre contrôleur de vue à partir d'un Storyboard ou NIB, ou l'avez-vous instancié directement via un initialiseur?

Si vous instanciez votre classe directement avec l'initialiseur, les prises ne seront pas connectées. Interface Builder crée des instances personnalisées de vos classes et encode ces instances en NIB et Storyboards pour un décodage répété, il ne définit pas les classes elles-mêmes. Si c'était votre problème, il vous suffit de changer le code dans lequel vous créez votre contrôleur pour utiliser à la place les méthodes sur UIStoryboard ou UINib.

Jon Hess
la source
2
Je crée une UIStoryboardinstance et j'y fais appel instantiateViewControllerWithIdentifier.
Stefan Arambasich
15

Le storyboard ne reconnaissait pas d'autres éléments de l'interface utilisateur que j'y ajoutais. Au moment de l'exécution, toutes les références étaient nulles. J'ai donc effacé mon dossier de données dérivées, puis ces connexions ont fonctionné à nouveau.

Stefan Arambasich
la source
2
Voilà la réponse. J'ai combattu cela pendant un bon moment, nettoyant le dossier de construction et tout. Dans mon cas, viewDidLoad()toutes les connexions ont été établies, mais viewWillAppear()presque toutes l'étaient nil(parfois un ou deux étaient encore connectés). J'ai tout essayé et finalement supprimé le dossier de données dérivé, reconstruit, et tout allait bien.
ruttopia
6
Même après 3 ans, Xcode 9.2 a toujours le même putain de bogue. Merci beaucoup.
Kemal Can Kaynak
1
Ici pour signaler que cela existe toujours dans Xcode 11. :(
passé le
13

Cela m'arrivait avec ma cellule de vue de collection personnalisée. Il s'avère que j'ai dû remplacer ma méthode registerClassforReuseIdentifier par registerNib. Cela m'a arrangé.

Rounak
la source
Vous avez raison. J'ai lu en détail le contenu de comptage ARC dans Swift Programming Language. Je ne trouve pas la raison pour laquelle la propriété faible IBOutlet est toujours nulle. Enfin, je recherche sur Google «@IBOutlet swift nil» et je trouve votre réponse dans SO. Ensuite, j'ai marqué la ligne Xcode template ajoutée 'self.collectionView! .RegisterClass' dans viewDidLoad. Ensuite, ça marche. Tu me sauves la vie.
charles.cc.hsu
12

Cela s'est produit pour moi parce que j'instanciais accidentellement mon contrôleur de vue directement au lieu de l'instancier via le storyboard. Si vous instanciez directement via, MyViewController()les prises ne seront pas connectées.

Eric Conner
la source
Si vous utilisez un fichier XIB, l'instanciation directe fonctionne toujours avec succès.
Luat Vu Dinh
11

Dans mon cas, cela s'est produit parce que loadViewj'ai remplacé la méthode dans ma sous-classe ViewController, mais j'ai oublié d'ajouter[super loadView]

-(void)loadView {
// blank
}

Lorsque vous remplacez la loadViewméthode, il est de votre responsabilité de lancer vos sous-vues. Comme vous le remplacez, les vues du générateur d'interface n'ont pas la possibilité de se convertir en objets cacao et les points de vente restent donc nuls.

Si vous implémentez loadView dans votre sous-classe de contrôleur de vue, il est de votre responsabilité de charger les éléments d'interface utilisateur de storyboard / xib dans le code.

Ou juste appeler

[super loadView];

Pour que la superclasse ait la chance de charger storyboard / xib dans le code.

zinnuree
la source
8

Vous pouvez appeler controller.viewpour forcer le chargement de la vue pour initialiser les IBOutlets, puis vous pourrez attribuer les valeurs.

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
    }
}
zs2020
la source
7

Si vous instanciez view controllervia un programme. Ensuite, essayez de le créer comme ci-dessous

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

au lieu de directement

let initialVC = InitialVC()

Cela a fonctionné pour moi.

Vinoth Vino
la source
6

Pour moi, cela s'est produit lorsque j'ai accidentellement déclaré la classe de mon contrôleur de vue comme

class XYZViewController: UINavigationController {
}

(c'est-à-dire comme un UINavigationControllerpas a UIViewController).

Xcode ne capte pas sur cette erreur, la classe semble bien de construction, et les fonctions de remplacement telles que viewDidLoad, viewWillAppearetc. correctement tous les travaux. Mais aucun des IBOutlets n'est connecté.

Changer la déclaration en

class XYZViewController: UIViewController {
}

corrigé complètement.

HughHughTeotl
la source
5

Je rencontre ce problème récemment! Voici ma pensée. Le problème ne concerne pas votre storyboard ou un problème de lien. Il s'agit de la manière dont vous lancez votre ViewController. Surtout lorsque vous utilisez Swift (il n'y a presque rien dans l'éditeur lorsque vous créez un fichier de classe)

En utilisant simplement la init()classe de super ne peut pas lancer quoi que ce soit que vous avez travaillé avec le storyboard. Vous devez donc modifier l'initialisation du ViewController. Remplacer let XXViewController = XXViewController() par let XXViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("XXViewController") as! XXViewController Ceci indique au programme d'aller dans le storyboard trouver XXViewController et lance tout IBOutletdans votre storyboard.

J'espère que cette aide ~ GL

user3882355
la source
Ce n'était pas le problème
Stefan Arambasich
4

2019, UNE POSSIBILITÉ POUR CE PROBLÈME HORRIBLE:

  • Supposons que vous ayez peut-être une vue de conteneur qui montre une sorte d'horloge. Alors tu as

    Horloge de classe: UIViewController

Vous l'utilisez en fait à plusieurs endroits de l'application .

Sur l'écran principal, sur l'écran des détails, sur l'écran d'édition.

Vous avez une application moderne de type Snapchat compliquée.

En fait, Clockpeut en fait être chargé plus d'une fois en même temps quelque part sur le même écran . (Peut-être que c'est caché dans certains cas.)

Vous commencez à travailler sur une instance de Clocksur l'un de vos nombreux storyboards.

Sur ce storyboard, vous ajoutez une étiquette, NewLabel.

Naturellement, vous ajoutez la sortie dans le code. Tout devrait fonctionner. Tous les autres points de vente fonctionnent parfaitement.

Vous avez définitivement lié le point de vente.

Mais l'application plante avec NewLabel comme nul.

Xcode vous indique clairement "vous avez oublié de connecter la prise".

La raison en est que .......... vous avez "NewLabel" sur une seule des utilisations du storyboard de Clock!

Le crash provient en fait >>> d'un autre endroit <<<< vous utilisez Clock !!!!

Xcode ne vous dit pas que le crash provient d' un autre endroit, pas de l'endroit où vous travaillez!

Le crash n'est en fait pas de l'endroit où vous travaillez - il vient d' un autre storyboard, où il n'y a pas d'élément "NewLabel" sur ce storyboard !!!

Frustrant.

Fattie
la source
3

Pour Swift 3.

func configureView() {
    let _  = self.view
}
prog_24
la source
2

Vous devez d'abord charger la hiérarchie de vues afin d'instancier les sorties dans le storyboard. Pour cela, vous pouvez appeler manuellement les méthodes loadView ou loadViewIfNeeded.

maven25
la source
2

Dans mon cas, l'application a commencé à planter tout d'un coup. Le débogage a révélé que tous les points de vente étaient encore nilau moment de viewDidLoad().

Mon application utilise toujours des pointes (pas des storyboards) pour la plupart des contrôleurs de vue. Tout était en place, toutes les prises câblées correctement. J'ai revérifié.

Nous instancions généralement nos contrôleurs de vue comme

let newVC = MYCustomViewController()

... qui , pour une raison qui semble fonctionner aussi longtemps que le .xib porte le même nom que la classe de contrôleur de vue (pas sûr de savoir comment cela fonctionne, bien. Nous ne réclamons init(nibName:bundle:)des arguments nuls ou impérieuses init()de le faire sur selfcomme il est généralement suggéré ...).

Alors j'ai essayé d'appeler explicitement

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

... seulement pour être accueilli avec l'erreur d'exception d'exécution:

*** Arrêt de l'application en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: 'Impossible de charger NIB dans le bundle:' NSBundle </ Users / nicolasmiari / Library / Developer / CoreSimulator / Devices / 3DA3CF21-108D-498F-9649-C4FC9E3C1A8D / data /Containers/Bundle/Application/C543DDC1-AE86-4D29-988C-9CCE89E23543/MyApp.app> (chargé) 'avec le nom' MYCustomViewController ''

Et puis , je l'ai vu:

La case à cocher "Adhésion cible" du fichier .xib a été décochée.


Doit s'être produit lors de la résolution de l'un des conflits de fusion fréquents concernant le fichier de projet Xcode.

Apple doit absolument proposer un format de fichier de projet plus compatible avec SCM.

Nicolas Miari
la source
2

Solution 100% fonctionnelle pour créer des ViewControllers à partir de XIB sans StoryBoards

  1. Créer la classe CustomViewController: UIViewController
  2. Créer une vue CustomViewControllerView.xib
  3. Dans CustomViewControllerView.xib dans Interface Builder, sélectionnez Espaces réservés -> Propriétaire du fichier
  4. Dans "Inspecteur d'attributs", définissez Class sur CustomViewController
  5. Dans «l'inspecteur de connexions», connectez «vue» à la vue de niveau supérieur de xib (assurez-vous que la classe de la vue de niveau supérieur ne pointe pas vers CustomViewController)
  6. Dans "Inspecteur des connexions", connectez d'autres prises (si nécessaire / existent)
  7. Créer une instance de CustomViewController dans le contrôleur de vue parent / délégué d'application

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")
Romain M
la source
Réponse fantastique, merci! Pour ce que cela vaut, vous pouvez également remplacer init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)et appeler l'étape 7.2 selfdirectement dans la classe CustomViewController.
brandonscript
1

Vérifiez votre connexion IBOutlet si elle est connectée au propriétaire du fichier ou à la vue. Il pourrait y avoir des erreurs.

Ahd Radwan
la source
1

Autre cas:

Vos points de vente ne seront pas définis tant que la vue du contrôleur de vue ne sera pas réellement instanciée, ce qui dans votre cas se produit probablement peu de temps après initWithNibName: bundle: —à quel point elles seront toujours nulles. Toute configuration que vous effectuez qui implique ces prises doit se produire dans la méthode -viewDidLoad de votre contrôleur de vue.

93sauu
la source
1

Pour moi, j'ai eu la même erreur sur un storyboard localisé, un élément a été ajouté dans certains paramètres régionaux et pas dans l'autre, donc j'avais une référence nulle pour cet élément lors du basculement vers les paramètres régionaux de l'élément manquant, j'ai dû supprimer la localisation (redondante) pour ce storyboard en utilisant https://stackoverflow.com/a/42256341/1356559 .

Amr Lotfy
la source
1

Pour moi, c'était un crash parce que containerViewc'était nul.

Voici mon code avec Crash .

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

La chose manquante appelait le super.loadView(). Donc l'ajouter a résolu le problème pour moi.

Code fixe :

@IBOutlet private var containerView: UIView!
override open func loadView() {
    super.loadView()
    containerView.addSubview(anotherView)
}
BLC
la source
vous ne devriez jamais appeler loadView () directement ( developer.apple.com/documentation/uikit/uiviewcontroller/... ), appelez self.view à la place
Lio
1

J'ai eu un problème similaire lorsque j'avais précédemment ajouté le registre (_: forCellReuseIdentifier :) pour la cellule personnalisée après avoir déjà défini l'identifiant dans le storyboard. Avait ce code dans la fonction viewDidLoad (). Une fois que je l'ai retiré, cela a bien fonctionné.

Gthoma2
la source
1

Encore un autre cas que je viens de rencontrer. J'ai changé le nom de ma classe pour l'UIViewController, mais j'ai oublié de changer le nom du fichier .xib où l'interface a été construite.

Une fois que j'ai attrapé cela et que les noms de fichiers reflètent le nom de la classe, tout était bon!

J'espère que cela aide quelqu'un.

Brian Trzupek
la source
1

J'en ai encore un ...

Si vous avez une classe personnalisée pour un UITableViewCell mais que vous oubliez de spécifier Custom dans le Style de la cellule.

Phantom59
la source
1

Vous pouvez valider si la vue est chargée.

if isViewLoaded && view.window != nil {
 //self.annotationOptionsView.
}
A. Trejo
la source
0
  1. sélectionnez les deux .het .maffichez les fichiers du contrôleur
  2. supprimer la référence de ces fichiers
  3. rajoutez les fichiers à votre arborescence de projet
  4. ouvrez le storyboard, recréez éventuellement le projet
Sandip Patel - SM
la source
0

J'ai accidentellement sous-classé mon contrôleur de vue avec AVPlayerViewControllerau lieu de UIViewController. En le rejouant pour UIViewControllerrevenir à la normale. Cela devrait aider.

Aucun nettoyage de build (normal et complet), la suppression des dossiers de données dérivés et la fermeture de Xcode a fonctionné pour moi.

Hemang
la source
0

J'ai eu le même problème après avoir copié une classe (liée à un xib) pour la réutiliser avec une autre classe de viewcontroller (liée à un storyboard). J'ai oublié de supprimer

override var nibName

et

override var nibBundle

méthodes.

Après les avoir supprimés, mes points de vente ont commencé à fonctionner.

smukamuka
la source
0

Je vois que vous utilisez ViewController!? en ViewControllerclasse, vous devez utiliser -viewDidLoad, non -awakeFromNib , -awakeFromNibutiliser pour la UIViewclasse

EvGeniy ​​Ilyin
la source
0

Vérifiez si vous avez des prises manquantes ou déconnectées.

entrez la description de l'image ici

Исмаил Хасбулатов
la source
0

Si vous en avez deux main.storyboardset que vous modifiez le mauvais, cela peut arriver. Cela peut se produire à chaque fois que vous connectez une prise à partir d'un non-instancié storyboard.

ScottyBlades
la source