IBOutlet es nulo, pero está conectado en el guión gráfico, Swift

102

Usando Swift 1.1 y Xcode 6.2.

Tengo un UIStoryboard que contiene una UIViewControllersubclase personalizada singular . En él, tengo una @IBOutletconexión de tipo UIViewdesde ese controlador a una UIView subclase en el guión gráfico. También tengo salidas similares para subvistas de esa vista. Ver figura A.

Pero en tiempo de ejecución, estas propiedades son nulas (Figura B). Aunque he asegurado que he conectado las salidas en Interface Builder.

Pensamientos :

  • ¿Es posible que debido a que estoy usando una subclase de una subclase, algo se estropee con la inicialización? No anularé ningún inicializador
  • awakeFromNib: no es llamado por alguna razón
  • Tal vez no se conecte a subvistas en subvistas

Cosas que he probado:

  • @IBOutletTipos de elementos coincidentes y del guión gráfico exactamente (en lugar de UIView)
  • Eliminar propiedad y salida y volver a agregarlos

Figura A

Figura A *

Figura B

Figura B

* El código oculto en Figure Aes:

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

Gracias.

Stefan Arambasich
fuente
¿Por qué no cambiar el! a ?
clearView es nulo porque no está vinculado al guión gráfico (vea el círculo a la izquierda del código con un agujero, que indica que no está vinculado), en la captura de pantalla no puedo ver la declaración de annotationOptionView.
Javier Flores Font
@JavierFloresFont: clearViewEspero ser nulo. Es algo que todavía tengo que refactorizar. Consulte también la nota al pie de la figura A. @ShaanSingh ¡Debería serlo! porque las conexiones de los guiones gráficos se establecen (se supone que están) en tiempo de ejecución y no deberían ser nulas.
Stefan Arambasich
¿Cómo se carga este controlador de vista? Muéstranos el código que lo solicita o describe el segue que se conecta a él.
rob mayoff
1
Está obteniendo el guión gráfico correcto: let vc = UIStoryboard(name: "LocationPickerModal", bundle: nil) .instantiateViewControllerWithIdentifier("LocationPickerModalViewController") as LocationPickerModalViewController
Stefan Arambasich

Respuestas:

146

Normalmente, esto sucede porque su controlador de vista aún no ha cargado su jerarquía de vista. Un controlador de vista solo carga su jerarquía de vista cuando algo le envía el viewmensaje. El sistema hace esto cuando es el momento de poner realmente la jerarquía de vista en la pantalla, lo que sucede después de que cosas como prepareForSegue:sender:y viewWillAppear:hayan regresado.

Dado que su VC aún no ha cargado su jerarquía de vistas, sus puntos de venta siguen siendo nulos.

Puede obligar al VC a cargar su jerarquía de vistas diciendo _ = self.view.

Rob Mayoff
fuente
4
Esto ocurre cuando viewWillAppear:se llama, la primera vez que hago algo con esa salida. Acceder a la vista no me hace nada. Todavía nulo.
Stefan Arambasich
¡Gracias! Esto funcionó para mí también. var v = viewcontroller.view; forzado a cargar la jerarquía de vistas. ¡Buen truco! ;)
Yordi Uytersprot
@YordiUytersprot, ¿dónde pondrías esta línea?
Async-
4
Bueno, si crea una instancia de un controlador de vista: let vc = self.storyboard? .InstantianteViewControllerWithIdentifier ("StoryboardIDfromVC") como? CustomVC; dejar ver = vc.view; // Ahora mismo, puede acceder a IBOutlets desde CustomVC aquí .. vc.customTextField.text = "Lalala"; ¡Espero que te ayude! :)
Yordi Uytersprot
4
UIViewcontroller.loadViewIfNeeded()es el método correcto para usar aquí.
Orkoden
27

¿Ha intentado ejecutar Producto> Limpiar? Me resolvió un problema muy similar.

ThottJefe
fuente
Yo hice. Lo siento, pretendía marcar mi respuesta. Solo comenzó a funcionar después de que eliminé la DerivedDatacarpeta del proyecto.
Stefan Arambasich
3
Solo dos de mis puntos de venta en el medio de mi jerarquía de vista eran nulos. Producto -> ¡La limpieza funcionó para mí!
Rikco
1
obj c, solo limpiar el proyecto funcionó para mí ... no puedo creer que pasé más de una hora y ni siquiera pensé en limpiar el proyecto: / ¡pero gracias!
invernadero
Solo producto -> Limpiar no funcionó para mí, pero comencé un simulador diferente y funcionó, por lo que también debe haber algo mal en la carpeta DerivedData.
endavid
22

¿Instaló su controlador de vista desde un Storyboard o NIB, o lo creó directamente a través de un inicializador?

Si creó una instancia de su clase directamente con el inicializador, las salidas no se conectarán. Interface Builder crea instancias personalizadas de sus clases y codifica esas instancias en NIB y Storyboards para decodificación repetida, no define las clases en sí. Si este fue su problema, solo necesita cambiar el código donde crea su controlador para usar los métodos en UIStoryboard o UINib.

Jon Hess
fuente
2
Estoy creando una UIStoryboardinstancia y la invoco instantiateViewControllerWithIdentifier.
Stefan Arambasich
15

El guión gráfico no reconocía más elementos de la interfaz de usuario que le agregué. En tiempo de ejecución, todas las referencias eran nulas. Así que borré mi carpeta de datos derivados y luego esas conexiones funcionaron nuevamente.

Stefan Arambasich
fuente
2
Esta es la respuesta. Luché contra esto durante bastante tiempo, limpiando la carpeta de compilación y todo. En mi caso, en viewDidLoad()todas las conexiones se hicieron, pero en viewWillAppear()casi todas sí nil(a veces una o dos seguían conectadas). Probé todo y finalmente eliminé la carpeta de datos derivados, la reconstruí y todo estuvo bien.
ruttopia
6
Incluso después de 3 años, Xcode 9.2 todavía tiene el mismo maldito error. Muchas gracias.
Kemal Can Kaynak
1
Aquí para informar que esto todavía existe en Xcode 11. :(
spentag
13

Esto me estaba sucediendo con mi celda de vista de colección personalizada. Resulta que tuve que reemplazar mi método registerClassforReuseIdentifier con registerNib. Eso me lo arregló.

rounak
fuente
Tienes razón. Leí el contenido de conteo ARC en Swift Programming Language en detalle. No puedo encontrar la razón por la cual la propiedad débil de IBOutlet sigue siendo nula. Por fin, busco en Google '@IBOutlet swift nil' y encuentro su respuesta en SO. Luego marqué la línea Xcode template agregada 'self.collectionView! .RegisterClass' en viewDidLoad. Entonces, funciona. Salvas mi vida.
charles.cc.hsu
12

Esto sucedió para mí porque accidentalmente estaba instanciando mi controlador de vista directamente en lugar de instanciarlo a través del guión gráfico. Si crea una instancia directamente a través de MyViewController(), las salidas no se conectarán.

Eric Conner
fuente
Si usa el archivo XIB, la instanciación directamente aún funciona correctamente.
Luat Vu Dinh
11

En mi caso, sucedió porque anulé el loadViewmétodo en mi subclase ViewController, pero olvidé agregar[super loadView]

-(void)loadView {
// blank
}

Cuando anula el loadViewmétodo, es su responsabilidad iniciar sus subvistas. Como lo anula, las vistas del generador de interfaces no tienen la oportunidad de convertirse en objetos cocoa y, por lo tanto, las salidas permanecen nulas.

Si implementa loadView en su subclase de controlador de vista, entonces es su responsabilidad cargar los elementos de la interfaz de usuario desde el guión gráfico / xib al código.

O simplemente llama

[super loadView];

Para que la superclase tenga la oportunidad de cargar el guión gráfico / xib en el código.

zinnuree
fuente
8

Puede llamar controller.viewpara forzar la carga de la vista para inicializar los IBOutlets, luego podrá asignar los valores.

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

Si crea una instancia view controllermediante programación. Entonces intente crearlo como a continuación

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

en lugar de directamente

let initialVC = InitialVC()

Esto funcionó para mí.

Vinoth Vino
fuente
6

Para mí, esto ocurrió cuando declaré accidentalmente la clase de mi controlador de vista como

class XYZViewController: UINavigationController {
}

(es decir, como UINavigationControllerno a UIViewController).

Xcode no se recupera en este error, la clase de construcción parece bien, y funciones de mando, tales como viewDidLoad, viewWillAppear, etc. todo el trabajo correctamente. Pero ninguno de los IBOutlets se conecta.

Cambiar la declaración a

class XYZViewController: UIViewController {
}

lo arregló por completo.

HughHughTeotl
fuente
5

¡Encontré este problema recientemente! Aquí está mi pensamiento. El problema no se trata de su guión gráfico ni de ningún problema de enlace. Se trata de cómo inicia su ViewController. Especialmente cuando estás usando Swift (apenas hay nada en el editor cuando creas un archivo de clase)

Con el simple uso de la init()superclase from no se puede iniciar nada que haya trabajado con el storyboard. Entonces, lo que debe hacer es cambiar la inicialización del ViewController. Reemplazar let XXViewController = XXViewController() por let XXViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("XXViewController") as! XXViewController Esto le dice al programa que vaya al guión gráfico, busque XXViewController e inicie todo IBOutleten su guión gráfico.

Espero que esto ayude ~ GL

usuario3882355
fuente
Ese no era el problema
Stefan Arambasich
4

2019, UNA POSIBILIDAD PARA ESTE HORRIBLE PROBLEMA:

  • Supongamos que tiene quizás una vista de contenedor que muestra una especie de reloj. Así que tienes

    clase Clock: UIViewController

De hecho, lo usa en varios lugares de la aplicación .

En la pantalla principal, en la pantalla de detalles, en la pantalla de edición.

Tienes una aplicación moderna complicada similar a Snapchat.

De hecho, es Clockposible que se carguen más de una vez al mismo tiempo en algún lugar de la misma pantalla . (Quizás esté oculto en algunos casos).

Empieza a trabajar en una instancia de Clockuno de sus muchos guiones gráficos.

En ese guión gráfico, agrega una etiqueta, NewLabel.

Naturalmente, agrega la salida en código. Todo debería funcionar. Todos los demás puntos de venta funcionan perfectamente.

Definitivamente has vinculado la salida.

Pero la aplicación se bloquea con NewLabel como nulo.

Xcode le dice claramente "se olvidó de conectar el tomacorriente".

La razón es esta ... ¡tienes "NewLabel" en solo uno de los usos del guión gráfico de Clock!

El bloqueo es en realidad de >>> otro lugar <<<< ¡¡¡¡¡estás usando Clock !!!!

Xcode no te dice que el bloqueo es de otro lugar, ¡ no de donde estás trabajando!

En realidad, el bloqueo no es del lugar en el que estás trabajando, ¡es de otro guión gráfico, donde no hay un elemento "NewLabel" en ese guión gráfico!

Frustrante.

Fattie
fuente
3

Para Swift 3.

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

Primero debe cargar la jerarquía de vistas para crear una instancia de los puntos de venta en el guión gráfico. Para esto, puede llamar manualmente a los métodos loadView o loadViewIfNeeded.

maven25
fuente
2

En mi caso, la aplicación comenzó a fallar de repente. La depuración reveló que todos los puntos de venta estaban todavía nilen el momento de viewDidLoad().

Mi aplicación todavía usa plumillas (no guiones gráficos) para la mayoría de los controladores de vista. Todo estaba en su lugar, todas las salidas conectadas correctamente. Lo comprobé dos veces.

Normalmente, instanciamos nuestros controladores de vista como

let newVC = MYCustomViewController()

... que por alguna razón parece que funciona siempre y cuando el .xib tiene el mismo nombre que la clase controlador de vista (no sé cómo funciona, sin embargo. Estamos no un llamado init(nibName:bundle:)con argumentos nulos, o anulando init()de hacerlo en selfguste se sugiere típicamente ...).

Así que intenté llamar explícitamente

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

... solo para ser recibido con el error de excepción de tiempo de ejecución:

*** Finalizando la aplicación debido a una excepción no detectada 'NSInternalInconsistencyException', motivo: 'No se pudo cargar NIB en el paquete:' NSBundle </ Users / nicolasmiari / Library / Developer / CoreSimulator / Devices / 3DA3CF21-108D-498F-9649-C4FC9E3C1A8D / data /Containers/Bundle/Application/C543DDC1-AE86-4D29-988C-9CCE89E23543/MyApp.app> (cargado) 'con el nombre' MYCustomViewController ''

Y luego , lo vi:

La casilla de verificación "Membresía de destino" del archivo .xib estaba desmarcada.


Debe haber ocurrido al resolver uno de los frecuentes conflictos de fusión relacionados con el archivo de proyecto de Xcode.

Apple definitivamente necesita crear un formato de archivo de proyecto que sea más compatible con SCM.

Nicolas Miari
fuente
2

Solución 100% funcional para crear ViewControllers desde XIB sin StoryBoards

  1. Crear clase CustomViewController: UIViewController
  2. Crear vista CustomViewControllerView.xib
  3. En CustomViewControllerView.xib en Interface Builder, seleccione Marcadores de posición -> Propietario del archivo
  4. En "Inspector de atributos", establezca Clase en CustomViewController
  5. En el "Inspector de conexiones", conecte la "vista" a la vista de nivel superior de xib (asegúrese de que la clase de la vista de nivel superior no apunte a CustomViewController)
  6. En "Inspector de conexiones", conecte otras salidas (si es necesario / existe)
  7. Cree una instancia de CustomViewController en el controlador de vista principal / delegado de la aplicación

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")
Roman M
fuente
Fantástica respuesta, gracias! Por lo que vale, también puede anular init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)y llamar al paso 7.2 selfdirectamente en la clase CustomViewController.
brandonscript
1

Verifique su conexión de IBOutlet si se conectó al propietario del archivo o la vista. Puede haber errores.

Ahd Radwan
fuente
1

Otro caso:

Sus puntos de venta no se configurarán hasta que se cree una instancia de la vista del controlador de vista, lo que en su caso probablemente sucederá poco después de initWithNibName: bundle: —en cuyo punto todavía serán nulos. Cualquier configuración que haga que involucre esos puntos de venta debería estar sucediendo en el método -viewDidLoad de su controlador de vista.

93sauu
fuente
1

Para mí, tuve el mismo error en un guión gráfico localizado, se agregó un elemento en alguna configuración regional y no en la otra, por lo que tenía una referencia nula para ese elemento cuando cambié a la ubicación del elemento faltante, tuve que eliminar la localización (redundante) para ese guión gráfico usando https://stackoverflow.com/a/42256341/1356559 .

Amr Lotfy
fuente
1

Para mí, esto se colapsaba porque containerViewera nulo.

Aquí está mi código con Crash .

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

Lo que faltaba era llamar al super.loadView(). Así que agregarlo resolvió el problema para mí.

Código fijo :

@IBOutlet private var containerView: UIView!
override open func loadView() {
    super.loadView()
    containerView.addSubview(anotherView)
}
BLC
fuente
nunca debe llamar a loadView () directamente ( developer.apple.com/documentation/uikit/uiviewcontroller/… ), llame a self.view en su lugar
Lio
1

Tuve un problema similar cuando agregué previamente el registro (_: forCellReuseIdentifier :) para la celda personalizada después de haber definido el identificador en el guión gráfico. Tenía este código en la función viewDidLoad (). Una vez que lo quité, funcionó bien.

Gthoma2
fuente
1

Otro caso más que acabo de encontrar. Cambié el nombre de mi clase por el UIViewController, pero olvidé cambiar el nombre del archivo .xib donde se construyó la interfaz.

Una vez que capté esto e hice que los nombres de los archivos reflejaran el nombre de la clase, ¡todo estuvo bien!

Espero que ayude a alguien.

Brian Trzupek
fuente
1

Tengo uno más ...

Si tiene una clase personalizada para un UITableViewCell pero se olvida de especificar Personalizado en el Estilo de la celda.

Phantom59
fuente
1

Puede validar si la vista is está cargada.

if isViewLoaded && view.window != nil {
 //self.annotationOptionsView.
}
A. Trejo
fuente
0
  1. seleccionar ambos .hy .mver los archivos del controlador
  2. eliminar la referencia de esos archivos
  3. vuelva a agregar los archivos a su árbol de proyecto
  4. abrir el guión gráfico, eventualmente reconstruir el proyecto
Sandip Patel - SM
fuente
0

Accidentalmente subclasé mi controlador de vista con en AVPlayerViewControllerlugar de UIViewController. Repitiéndolo a todo UIViewControllernormal. Esto debería ayudar.

Sin limpieza de compilación (normal y completa), eliminar carpetas de datos derivadas y salir de Xcode funcionó para mí.

Hemang
fuente
0

Tuve el mismo problema después de copiar una clase (vinculada a un xib) para reutilizarla con otra clase de controlador de vista (vinculada a un guión gráfico). Me olvidé de quitar

override var nibName

y

override var nibBundle

métodos.

Después de quitarlos, mis enchufes comenzaron a funcionar.

smukamuka
fuente
0

¡Te veo usar ViewController!? en ViewControllerclase debes usar -viewDidLoad, no -awakeFromNib , -awakeFromNibusar para UIViewclase

EvGeniy ​​Ilyin
fuente
0

Verifique si tiene algún tomacorriente faltante o desconectado.

ingrese la descripción de la imagen aquí

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

Si tiene dos main.storyboardsy está haciendo cambios en el incorrecto, esto puede suceder. Esto puede suceder en cualquier momento que conecte una toma de un no autenticado storyboard.

ScottyBlades
fuente