Storyboard - consulte ViewController no AppDelegate

122

considere o seguinte cenário: Eu tenho um aplicativo baseado em storyboard. Eu adiciono um objeto ViewController ao storyboard, adiciono os arquivos de classe para este ViewController ao projeto e especifico o nome da nova classe no inspetor de identidade da IB. Agora, como vou me referir a este ViewController programaticamente a partir do AppDelegate? Fiz uma variável com a classe relevante e a transformei em uma propriedade IBOutlet, mas não vejo nenhuma maneira de poder me referir ao novo ViewController no código - qualquer tentativa de arrastar uma conexão com a tecla Ctrl não funciona .

ou seja, dentro do AppDelegate, posso acessar o ViewController base como este

(MyViewController*) self.window.rootViewController

mas e qualquer outro ViewController contido no storyboard?

Matthias D
fonte
Verifique esta resposta . É rápido, mas os idiomas são semelhantes.
Borzh

Respostas:

165

Dê uma olhada na documentação para -[UIStoryboard instantiateViewControllerWithIdentifier:]. Isso permite instanciar um controlador de exibição do storyboard usando o identificador que você definiu no IB Attributes Inspector:

insira a descrição da imagem aqui

EDITADO para adicionar código de exemplo:

UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                                         bundle: nil];

MyViewController *controller = (MyViewController*)[mainStoryboard 
                    instantiateViewControllerWithIdentifier: @"<Controller ID>"];
Robin Summerhill
fonte
Oi Robin, obrigado por isso! Eu olhei para este documento, mas misturei as palavras instanciar e inicializar ... isso nos leva até lá (depois de seguir suas instruções :) (maldita falta de formatação de código nas respostas ...) UIStoryboard * mainStoryboard = [UIStoryboard storyboardWithName: @ Pacote "MainStoryboard": nil]; MyViewController * thisController = (MyViewController *) [mainStoryboard instantiateViewControllerWithIdentifier: @ "myvc"];
Matthias D
Adicionei seu código de exemplo à resposta com formatação para quem está olhando para isso.
quer
24
se você estiver criando um aplicativo universal, use MainStoryboard_iPhone / MainStoryboard_iPad, caso contrário, ocorrerá um acidente.
roocell
16
De dentro do delegado, você pode acessar a instância do storyboard carregada pelo seu info.plist da seguinte forma: [[[self window] rootViewController] storyboard] De acordo com os documentos, isso retornará o "storyboard do qual o controlador de exibição se originou". (ou nulo se não vier de um storyboard). Nesse UIStoryboard *, você pode usar as chamadas instanciadas mencionadas pelo @RobinSummerhill. Observe que os Storyboards instanciam novas instâncias dos seus viewControllers ( cenas ) conforme elas são necessárias e não reutilizam as que foram visualizadas anteriormente.
Tad Bumcrot
6
Acredito que o OP queira saber COMO CHEGAR AO CORRENTE ATUALMENTE vc. Não como carregar um. Exatamente como ele explica, você costumava dizer esse self.window.rootViewController e agora não pode mais.
Fattie
41

Se você usa XCode5, deve fazê-lo de uma maneira diferente.

  • Escolha a sua UIViewControlleremUIStoryboard
  • Vá para o Identity Inspectorpainel superior direito
  • Marque a Use Storyboard IDcaixa de seleção
  • Escreva um ID exclusivo para o Storyboard IDcampo

Então escreva seu código.

// Override point for customization after application launch.

if (<your implementation>) {
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" 
                                                             bundle: nil];
    YourViewController *yourController = (YourViewController *)[mainStoryboard 
      instantiateViewControllerWithIdentifier:@"YourViewControllerID"];
    self.window.rootViewController = yourController;
}

return YES;
Faruk Toptas
fonte
4
Tanto quanto eu sei e foi capaz de experimentá-lo, esta é a resposta atualizada.
Gnclmorais # 21/13
O nome de um storyboard é o nome do arquivo sem a extensão, portanto também pode ser "Main_iPhone" ou "Main_iPad" se você configurou seu projeto dessa maneira.
Brian White
e então como fazemos a transição de volta para o rootViewController original após o login. Pelo menos para o meu conhecimento do iOS 7 e Xcode 5.0.2 - a alteração do controlador de exibição raiz será imediata e sem animação mudará a hierarquia de exibição. A equipe UX assassinou codificadores por menos.
lol
Como eu faria isso usando o Swift?
Leo Dabus
8

Geralmente, o sistema deve lidar com a instanciação do controlador de exibição com um storyboard. O que você deseja é atravessar a hierarquia do viewController, pegando uma referência ao self.window.rootViewControllerem vez de inicializar os controladores de exibição, que já devem ser inicializados corretamente se você configurou seu storyboard corretamente.

Então, digamos que você rootViewControlleré um UINavigationController e, em seguida, você deseja enviar algo para o seu controlador de vista superior, faça o seguinte no AppDelegate didFinishLaunchingWithOptions:

UINavigationController *nav = (UINavigationController *) self.window.rootViewController;
MyViewController *myVC = (MyViewController *)nav.topViewController;
myVC.data = self.data;

Em Swift, se seria muito semelhante:

let nav = self.window.rootViewController as! UINavigationController;
let myVC = nav.topViewController as! MyViewController
myVc.data = self.data

Você realmente não deve inicializar os controladores de exibição usando os IDs do storyboard do representante do aplicativo, a menos que queira ignorar a maneira normal como o storyboard é carregado e carregar o storyboard inteiro. Se você estiver tendo que inicializar cenas do AppDelegate, provavelmente está fazendo algo errado. Quero dizer, imagine que, por algum motivo, você deseja enviar dados para um controlador de exibição na pilha, o AppDelegate não deve estar chegando à pilha do controlador de exibição para definir dados. Isso não é da sua conta. Seu negócio é o rootViewController. Deixe o rootViewController manipular seus próprios filhos! Portanto, se eu estivesse ignorando o processo normal de carregamento de storyboard pelo sistema removendo referências a ele no arquivo info.plist, instanciaria no máximo o rootViewController usandoinstantiateViewControllerWithIdentifier:e, possivelmente, sua raiz, se for um contêiner, como um UINavigationController. O que você deseja evitar é instanciar controladores de exibição que já foram instanciados pelo storyboard. Este é um problema que vejo muito. Em resumo, discordo da resposta aceita. Ele está incorreto, a menos que os pôsteres signifiquem remover o carregamento do storyboard do info.plist, pois você carregou 2 storyboards de outra forma, o que não faz sentido. Provavelmente não é um vazamento de memória porque o sistema inicializou a cena raiz e a atribuiu à janela, mas você apareceu e a instanciaram novamente e as atribuiu novamente. Seu aplicativo está com um péssimo começo!

smileBot
fonte
0
    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Tutorial" bundle:nil];
    self.window.rootViewController = [storyboard instantiateInitialViewController];
Ofir Malachi
fonte