Storyboard - zapoznaj się z ViewController w AppDelegate

122

rozważ następujący scenariusz: Mam aplikację opartą na scenorysach. Dodaję obiekt ViewController do scenorysu, dodaję pliki klas dla tego ViewController do projektu i określam nazwę nowej klasy w inspektorze tożsamości IB. Jak mam zamiar programowo odwoływać się do tego ViewController z AppDelegate? Utworzyłem zmienną z odpowiednią klasą i przekształciłem ją we właściwość IBOutlet, ale nie widzę żadnego sposobu, aby móc odwoływać się do nowego ViewController w kodzie - żadna próba przeciągnięcia połączenia przy użyciu klawisza Ctrl nie działa .

tj. w ramach AppDelegate mogę dostać się do podstawowego ViewController w ten sposób

(MyViewController*) self.window.rootViewController

ale co z każdym innym ViewController zawartym w serii ujęć?

Matthias D.
źródło
Sprawdź tę odpowiedź . Jest szybki, ale języki są podobne.
Borzh

Odpowiedzi:

165

Zapoznaj się z dokumentacją dotyczącą -[UIStoryboard instantiateViewControllerWithIdentifier:]. Pozwala to na utworzenie instancji kontrolera widoku ze scenorysu przy użyciu identyfikatora ustawionego w Inspektorze atrybutów IB:

wprowadź opis obrazu tutaj

EDYTOWANO, aby dodać przykładowy kod:

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

MyViewController *controller = (MyViewController*)[mainStoryboard 
                    instantiateViewControllerWithIdentifier: @"<Controller ID>"];
Robin Summerhill
źródło
Cześć Robin, dzięki za to! Spojrzałem na ten dokument, ale pomieszały się słowa: instancja i inicjalizacja ... to nas tam (po wykonaniu instrukcji :) (cholera, brak formatowania kodu w odpowiedziach ...) UIStoryboard * mainStoryboard = [UIStoryboard storyboardWithName: @ Pakiet "MainStoryboard": nil]; MyViewController * thisController = (MyViewController *) [mainStoryboard instantiateViewControllerWithIdentifier: @ "myvc"];
Matthias D
Dodałem Twój przykładowy kod do odpowiedzi z formatowaniem dla każdego, kto na to patrzy.
Robin Summerhill,
24
jeśli tworzysz uniwersalną aplikację, pamiętaj, aby użyć MainStoryboard_iPhone / MainStoryboard_iPad, w przeciwnym razie nastąpi awaria.
roocell
16
Z poziomu delegata możesz uzyskać dostęp do instancji scenorysu załadowanej przez twoją info.plist w następujący sposób: [[[self window] rootViewController] storyboard] Zgodnie z dokumentami zwróci to „scenorys, z którego pochodzi kontroler widoku”. (lub zero, jeśli nie pochodzi ze scenorysu). Z tej tablicy UIStoryboard * możesz użyć wywołań instancji, o których wspomniał @RobinSummerhill. Zauważ, że Storyboardy tworzą nowe wystąpienia twoich viewControllers ( scen ), gdy są potrzebne, i nie używają ponownie tych, które były wcześniej przeglądane.
Tad Bumcrot
6
Uważam, że OP chce wiedzieć, JAK DOJECHAĆ DO OBECNIE DZIAŁAJĄCEGO vc. Nie jak go załadować. Dokładnie tak, jak wyjaśnia, kiedyś można było powiedzieć self.window.rootViewController, a teraz już nie można.
Fattie
41

Jeśli używasz XCode5, powinieneś to zrobić w inny sposób.

  • Wybierz swój UIViewControllerwUIStoryboard
  • Przejdź do Identity Inspectorprawego górnego okienka
  • Zaznacz pole Use Storyboard IDwyboru
  • Wpisz unikalny identyfikator w Storyboard IDpolu

Następnie napisz swój kod.

// 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
źródło
4
O ile wiem, i był w stanie go wypróbować, to jest aktualizowana odpowiedź.
gnclmorais
Nazwa storyboardu to nazwa pliku bez rozszerzenia, więc może to być również „Main_iPhone” lub „Main_iPad”, jeśli skonfigurowałeś swój projekt w ten sposób.
Brian White,
a następnie w jaki sposób powrócimy do oryginalnego rootViewController po zalogowaniu. Przynajmniej według mojej wiedzy na temat iOS 7 i Xcode 5.0.2 - zmiana głównego kontrolera widoku z powrotem spowoduje natychmiastową i bez animacji zmianę hierarchii widoku. Zespół UX mordował programistów za mniej.
lol
Jak mam to zrobić za pomocą Swift?
Leo Dabus
8

Ogólnie system powinien obsługiwać tworzenie instancji kontrolera widoku z scenorysem. To, czego chcesz, to przechodzenie przez hierarchię viewController, przechwytując odwołanie do self.window.rootViewControllerkontrolerów widoku, w przeciwieństwie do inicjowania kontrolerów widoku, które powinny być już poprawnie zainicjowane, jeśli poprawnie skonfigurowałeś scenorys.

Powiedzmy, że rootViewControllerjest to kontroler UINavigationController, a następnie chcesz wysłać coś do kontrolera widoku z góry, zrobiłbyś to w następujący sposób w AppDelegate didFinishLaunchingWithOptions:

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

W Swift, jeśli byłoby bardzo podobne:

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

Naprawdę nie powinieneś inicjować kontrolerów widoku przy użyciu identyfikatorów scenorysu od delegata aplikacji, chyba że chcesz ominąć normalny sposób ładowania scenorysu i samodzielnie załadować całą scenorys. Jeśli musisz zainicjować sceny z AppDelegate, najprawdopodobniej robisz coś nie tak. Chodzi mi o to, że wyobraź sobie, że z jakiegoś powodu chcesz wysłać dane do kontrolera widoku w dół stosu, AppDelegate nie powinien sięgać daleko do stosu kontrolera widoku, aby ustawić dane. To nie jego sprawa. To biznes to rootViewController. Pozwól, aby rootViewController obsługiwał własne elementy podrzędne! Tak więc, gdybym ominął normalny proces ładowania scenorysu przez system, usuwając odniesienia do niego w pliku info.plist, co najwyżej utworzyłbym instancję rootViewController za pomocąinstantiateViewControllerWithIdentifier:i prawdopodobnie jego katalog główny, jeśli jest to kontener, taki jak UINavigationController. To, czego chcesz uniknąć, to tworzenie wystąpień kontrolerów widoku, które zostały już utworzone przez scenorys. To jest problem, który widzę często. Krótko mówiąc, nie zgadzam się z zaakceptowaną odpowiedzią. Jest to niepoprawne, chyba że plakaty oznaczają usunięcie ładowania storyboardu z info.plist, ponieważ w przeciwnym razie załadujesz 2 storyboardy, co nie ma sensu. Prawdopodobnie nie jest to wyciek pamięci, ponieważ system zainicjował scenę główną i przypisał ją do okna, ale potem przyszedłeś, ponownie utworzyłeś instancję i przypisałeś ją ponownie. Twoja aplikacja ma zły start!

smileBot
źródło
0
    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Tutorial" bundle:nil];
    self.window.rootViewController = [storyboard instantiateInitialViewController];
Ofir Malachi
źródło