IBOutlet é nulo, mas está conectado no storyboard, Swift

102

Usando Swift 1.1 e Xcode 6.2.

Eu tenho um UIStoryboard contendo uma UIViewControllersubclasse única e personalizada . Nele, eu tenho um @IBOutlettipo de conexão UIViewdesse controlador para uma UIView subclasse no storyboard. Eu também tenho saídas semelhantes para subviews dessa visão. Veja a figura A.

Mas em tempo de execução, essas propriedades são nulas (Figura B). Embora eu tenha garantido que conectei as tomadas no Interface Builder.

Pensamentos :

  • É possível que, por estar usando uma subclasse de uma subclasse, algo atrapalhe a inicialização? Não estou substituindo nenhum inicializador
  • awakeFromNib: não está sendo chamado por algum motivo
  • Talvez não conecte a subvisualizações em subvisualizações

Coisas que tentei:

  • @IBOutletTipos de itens correspondentes e de storyboard exatamente (em vez de UIView)
  • Excluir propriedade e saída e adicioná-los novamente

Figura A

Figura A *

Figura B

Figura B

* O código obscuro em Figure Aé:

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

Obrigado.

Stefan Arambasich
fonte
Por que não mudar o! para ?
clearView é nulo porque não está vinculado ao storyboard (veja o círculo à esquerda do código com um furo, que indica que não está vinculado), na imagem não consigo ver a declaração de annotationOptionView.
Javier Flores Font
@JavierFloresFont: clearViewEspero ser nulo. É algo que ainda preciso refatorar. Veja também a nota de rodapé da figura A. @ShaanSingh Deve ser! porque as conexões de storyboards são (supostamente) definidas no tempo de execução e não devem ser nulas.
Stefan Arambasich
Como este controlador de visualização é carregado? Mostre-nos o código que o pede ou descreva a segue que se conecta a ele.
rob mayoff
1
Ele está obtendo o storyboard certo: let vc = UIStoryboard(name: "LocationPickerModal", bundle: nil) .instantiateViewControllerWithIdentifier("LocationPickerModalViewController") as LocationPickerModalViewController
Stefan Arambasich

Respostas:

146

Normalmente, isso acontece porque seu controlador de visualização ainda não carregou sua hierarquia de visualização. Um controlador de visão só carrega sua hierarquia de visão quando algo lhe envia a viewmensagem. O sistema faz isso quando é hora de realmente colocar a hierarquia de visualizações na tela, o que acontece depois que coisas como prepareForSegue:sender:e viewWillAppear:retornaram.

Como seu VC ainda não carregou sua hierarquia de visualização, seus pontos de venda ainda são nulos.

Você poderia forçar o VC a carregar sua hierarquia de visualizações dizendo _ = self.view.

rob mayoff
fonte
4
Isso está ocorrendo quando viewWillAppear:é chamado, a primeira vez que faço alguma coisa com aquela tomada. Acessar a vista não faz nada para mim. Ainda nulo.
Stefan Arambasich
Obrigado! Isso funcionou para mim também. var v = viewcontroller.view; forçado a carregar a hierarquia de exibição. Belo truque! ;)
Yordi Uytersprot
@YordiUytersprot onde você colocaria esta linha?
Assíncrono
4
Bem, se você instanciar um viewcontroller: let vc = self.storyboard? .InstantianteViewControllerWithIdentifier ("StoryboardIDfromVC") como? CustomVC; let view = vc.view; // Agora, você pode acessar IBOutlets de CustomVC aqui .. vc.customTextField.text = "Lalala"; Espero que ajude você! :)
Yordi Uytersprot
4
UIViewcontroller.loadViewIfNeeded()é o método certo para usar aqui.
orkoden
27

Você já tentou executar Product> Clean. Resolvido um problema muito semelhante para mim.

ThottChief
fonte
Eu fiz. Desculpe, pretendia marcar minha resposta. Ele só começou a funcionar depois que excluí a DerivedDatapasta do projeto.
Stefan Arambasich
3
Apenas dois dos meus pontos de venda no meio da minha hierarquia de visualizações eram nulos. Produto -> Limpar funcionou para mim!
Rikco de
1
obj c, só limpar o projeto funcionou para mim ... não acredito que passei mais de uma hora e nem pensei em limpar o projeto: / mas obrigado !!
estufa
Just Product -> Clean não funcionou para mim, mas eu iniciei um simulador diferente e funcionou, então deve haver algo errado na pasta DerivedData também.
endavid
22

Você instanciou seu controlador de visualização a partir de um Storyboard ou NIB, ou instanciou-o diretamente por meio de um inicializador?

Se você instanciar sua classe diretamente com o inicializador, as saídas não serão conectadas. O Interface Builder cria instâncias personalizadas de suas classes e codifica essas instâncias em NIBs e Storyboards para decodificação repetida, não define as próprias classes. Se este foi o seu problema, você só precisa alterar o código onde você cria seu controlador para usar os métodos em UIStoryboard ou UINib.

Jon Hess
fonte
2
Estou criando uma UIStoryboardinstância e chamando instantiateViewControllerWithIdentifier-a.
Stefan Arambasich
15

O storyboard não estava reconhecendo nenhuma outra coisa da interface do usuário que eu adicionei a ele. Em tempo de execução, todas as referências eram nulas. Então, limpei minha pasta de dados derivados e essas conexões funcionaram novamente.

Stefan Arambasich
fonte
2
Esta é a resposta. Lutei contra isso por um bom tempo, limpando a pasta de compilação e tudo mais. No meu caso, em viewDidLoad()todas as conexões foram feitas, mas em viewWillAppear()quase todas foram nil(às vezes uma ou duas ainda estavam conectadas). Tentei de tudo e finalmente apaguei a pasta de dados derivada, reconstruí, e tudo estava bem.
ruttopia de
6
Mesmo depois de 3 anos, o Xcode 9.2 ainda tem o mesmo maldito bug. Muito obrigado.
Kemal Can Kaynak
1
Aqui para relatar que isso ainda existe no Xcode 11. :(
gastouag
13

Isso estava acontecendo comigo com minha célula de visualização de coleção personalizada. Acontece que tive que substituir meu método registerClassforReuseIdentifier por registerNib. Isso resolveu para mim.

rounak
fonte
Você está certo. Eu li o conteúdo de contagem ARC em linguagem de programação Swift em detalhes. Não consigo encontrar a razão pela qual a propriedade IBOutlet fraca ainda é nula. Por fim, pesquiso '@IBOutlet swift nil' no Google e encontro sua resposta em SO. Em seguida, marquei a linha do modelo Xcode adicionado 'self.collectionView! .RegisterClass' em viewDidLoad. Então, funciona. Você salva minha vida.
charles.cc.hsu
12

Isso aconteceu comigo porque acidentalmente instanciei meu controlador de visualização diretamente, em vez de instanciá-lo por meio do storyboard. Se você instanciar diretamente via MyViewController(), as tomadas não serão conectadas.

Eric Conner
fonte
Se você usar o arquivo XIB, instanciar diretamente ainda funciona com sucesso.
Luat Vu Dinh
11

No meu caso, aconteceu porque eu substituí o loadViewmétodo na minha subclasse ViewController, mas esqueci de adicionar[super loadView]

-(void)loadView {
// blank
}

Quando você substitui o loadView método, é sua responsabilidade inicializar suas subvisualizações. Como você o substitui, as visualizações do construtor de interface não têm a chance de se converter em objetos de cacau e, portanto, as saídas permanecem nulas.

Se você implementar loadView em sua subclasse de controlador de visualização, então será sua responsabilidade carregar os elementos de UI do storyboard / xib para o código.

Ou apenas ligue

[super loadView];

Para que a superclasse tenha a chance de carregar o storyboard / xib no código.

zinnuree
fonte
8

Você pode chamar o controller.viewforce para carregar a view para inicializar os IBOutlets, então você poderá atribuir os 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
fonte
7

Se você instanciar view controllerpor meio de programação. Em seguida, tente criá-lo como abaixo

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

em vez de diretamente

let initialVC = InitialVC()

Isso funcionou para mim.

Vinoth Vino
fonte
6

Para mim, isso ocorreu quando eu acidentalmente declarei a classe do meu controlador de visualização como

class XYZViewController: UINavigationController {
}

(ou seja, como UINavigationControllernão a UIViewController).

O Xcode não pegar esse erro, a classe parece construir bem, e funções de substituição, tais como viewDidLoad, viewWillAppear, etc. todo o trabalho corretamente. Mas nenhum dos IBOutlets se conecta.

Alterando a declaração para

class XYZViewController: UIViewController {
}

consertou completamente.

HughHughTeotl
fonte
5

Eu encontrei esse problema recentemente! Aqui está meu pensamento. O problema não é sobre seu storyboard ou qualquer problema de link. É sobre como você inicia seu ViewController. Especialmente quando você está usando o Swift. (Quase não há nada no editor quando você cria um arquivo de classe)

Simplesmente usando a init()classe from super não é possível iniciar nada do que você trabalhou com o storyboard. Então, o que você precisa fazer é alterar a inicialização do ViewController. Substituir let XXViewController = XXViewController() por let XXViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("XXViewController") as! XXViewController Isso diz ao programa para ir para o storyboard localizar XXViewController e iniciar tudo IBOutletem seu storyboard.

Espero esta ajuda ~ GL

user3882355
fonte
Esse não era o problema
Stefan Arambasich
4

2019, UMA POSSIBILIDADE PARA ESTE HORRÍVEL PROBLEMA:

  • Digamos que você tenha uma visualização de contêiner que mostra algum tipo de relógio. Então você tem

    classe Clock: UIViewController

Na verdade, você o usa em vários lugares no aplicativo .

Na tela principal, na tela de detalhes, na tela de edição.

Você tem um aplicativo moderno tipo snapchat complicado.

Na verdade, Clockpode realmente ser carregado mais de uma vez ao mesmo tempo em algum lugar na mesma tela . (Talvez esteja oculto em alguns casos.)

Você começa a trabalhar em uma instância de Clock em um de seus muitos storyboards.

Nesse storyboard, você adiciona um rótulo, NewLabel.

Naturalmente, você adiciona a saída em código. Tudo deve funcionar. Todas as outras tomadas funcionam perfeitamente.

Você definitivamente ligado à tomada.

Mas o aplicativo falha com o NewLabel como nulo.

O Xcode diz claramente "você se esqueceu de conectar a tomada".

A razão é esta .......... você tem "NewLabel" em apenas um dos usos do storyboard do Relógio!

A falha é na verdade de >>> outro lugar <<<< você está usando o Relógio !!!!

O Xcode não informa que a falha é de outro lugar, não de onde você está trabalhando!

A falha não é, na verdade, do local em que você está trabalhando - é de outro storyboard, onde não há item "NewLabel" nesse storyboard !!!

Frustrante.

Fattie
fonte
3

Para Swift 3.

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

Você precisa carregar a hierarquia de visualização primeiro para instanciar as saídas no storyboard. Para isso, você pode chamar manualmente os métodos loadView ou loadViewIfNeeded.

maven25
fonte
2

No meu caso, o aplicativo começou a travar de repente. A depuração revelou que todos os pontos de venda ainda estavam nilno momento de viewDidLoad().

Meu aplicativo ainda usa pontas (não storyboards) para a maioria dos controladores de visualização. Tudo estava no lugar, todas as tomadas devidamente conectadas. Eu verifiquei duas vezes.

Normalmente instanciamos nossos controladores de visualização como

let newVC = MYCustomViewController()

... que por algum motivo parece funcionar, desde que o .xib é nomeado o mesmo que a classe controlador de exibição (não sei como isso funciona, no entanto. Estamos não chamar init(nibName:bundle:)com argumentos zero, ou substituindo init()a fazê-lo em selfcomo ele normalmente é sugerido ...).

Tentei chamar explicitamente

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

... apenas para ser saudado com o erro de exceção de tempo de execução:

*** Encerrando o aplicativo devido à exceção não detectada 'NSInternalInconsistencyException', motivo: 'Não foi possível carregar o NIB no pacote:' NSBundle </ Users / nicolasmiari / Library / Developer / CoreSimulator / Devices / 3DA3CF21-108D-498F-9649-C4FC9E3C1A8D / data /Containers/Bundle/Application/C543DDC1-AE86-4D29-988C-9CCE89E23543/MyApp.app> (carregado) 'com o nome' MYCustomViewController ''

E então , eu vi:

A caixa de seleção "Associação de destino" do arquivo .xib foi desmarcada.


Deve ter acontecido ao resolver um dos conflitos de mesclagem frequentes em relação ao arquivo de projeto Xcode.

A Apple definitivamente precisa apresentar um formato de arquivo de projeto que seja mais compatível com o SCM.

Nicolas Miari
fonte
2

Solução 100% funcional para criar ViewControllers a partir de XIB sem StoryBoards

  1. Crie a classe CustomViewController: UIViewController
  2. Criar visualização CustomViewControllerView.xib
  3. Em CustomViewControllerView.xib no Interface Builder, selecione Placeholders -> File's Owner
  4. Em "Attributes inspector" defina Class como CustomViewController
  5. Em "Inspetor de conexões", conecte "visualização" à visualização de nível superior do xib (certifique-se de que a classe da visualização de nível superior não esteja apontando para CustomViewController)
  6. Em "Inspetor de conexões" conecte outras tomadas (se necessário / existir)
  7. Crie uma instância de CustomViewController no controlador de visualização pai / App delegado

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
fonte
Resposta fantástica, obrigado! Por que vale a pena, você também pode substituir init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)e chamar a etapa 7.2 selfdiretamente na classe CustomViewController.
brandonscript
1

Verifique se sua conexão IBOutlet se conectou ao proprietário do arquivo ou à visualização. Pode haver erros.

Ahd Radwan
fonte
1

Outro caso:

Seus pontos de venda não serão configurados até que a visão do controlador de visualização seja realmente instanciada, o que no seu caso provavelmente está acontecendo logo após initWithNibName: bundle: — em cujo ponto eles ainda serão nulos. Qualquer configuração que você fizer que envolva essas saídas deve ocorrer no método -viewDidLoad do controlador de visualização.

93sauu
fonte
1

Para mim, eu tive o mesmo erro em um storyboard localizado, um elemento foi adicionado em algum local e não em outro, então eu tinha uma referência nula para esse elemento quando alternado para o local do elemento ausente, tive que remover a localização (redundante) para esse storyboard usando https://stackoverflow.com/a/42256341/1356559 .

Amr Lotfy
fonte
1

Para mim, isso estava travando porque containerViewera nulo.

Aqui está meu código com Crash .

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

O que faltava era ligar para o super.loadView(). Então, adicioná-lo resolveu o problema para mim.

Código Fixo :

@IBOutlet private var containerView: UIView!
override open func loadView() {
    super.loadView()
    containerView.addSubview(anotherView)
}
BLC
fonte
você nunca deve chamar loadView () diretamente ( developer.apple.com/documentation/uikit/uiviewcontroller/… ), chame self.view em vez disso
Lio
1

Tive um problema semelhante ao adicionar o registro (_: forCellReuseIdentifier :) para a célula personalizada, depois de já ter definido o identificador no storyboard. Tinha este código na função viewDidLoad (). Depois de removê-lo, funcionou bem.

Gthoma2
fonte
1

Mais um caso que acabei de encontrar. Mudei o nome da minha classe para o UIViewController, mas esqueci de mudar o nome do arquivo .xib onde a interface foi construída.

Depois de perceber isso e fazer com que os nomes dos arquivos refletissem o nome da classe, estava tudo certo!

Espero que ajude alguém.

Brian Trzupek
fonte
1

Tenho mais um ...

Se você tiver uma classe personalizada para um UITableViewCell, mas se esqueça de especificar Custom no estilo da célula.

Phantom59
fonte
1

Você pode validar se o modo de exibição está carregado.

if isViewLoaded && view.window != nil {
 //self.annotationOptionsView.
}
A. Trejo
fonte
0
  1. selecione ambos .he .mveja os arquivos do controlador
  2. remova a referência desses arquivos
  3. adicionar novamente os arquivos à árvore do seu projeto
  4. abra o storyboard e, eventualmente, reconstrua o projeto
Sandip Patel - SM
fonte
0

Sem querer , criei uma subclasse do meu controlador de visualização com em AVPlayerViewControllervez de UIViewController. Repetindo as UIViewControllercoisas de volta ao normal. Isso deve ajudar.

Nenhuma limpeza de compilação (normal e completa), remover pastas de dados derivadas e sair do Xcode funcionou para mim.

Hemang
fonte
0

Tive o mesmo problema depois de copiar uma classe (vinculada a um xib) para reutilizá-la com outra classe viewcontroller (vinculada a um storyboard). Esqueci de remover

override var nibName

e

override var nibBundle

métodos.

Depois de removê-los, minhas tomadas começaram a funcionar.

smukamuka
fonte
0

Eu vejo que você usa ViewController!? na ViewControlleraula você deve usar -viewDidLoad, não -awakeFromNib , -awakeFromNibusar para a UIViewaula

EvGeniy ​​Ilyin
fonte
0

Verifique se há alguma tomada ausente ou desconectada.

insira a descrição da imagem aqui

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

Se você tiver dois main.storyboardse estiver fazendo alterações no errado, isso pode acontecer. Isso pode acontecer sempre que você conectar uma tomada de um não instanciado storyboard.

ScottyBlades
fonte