Widok iPhone'a Wygląda na to, że nie działa

116

Czytałem wiele postów na temat osób mających problemy z viewWillAppear, jeśli nie utworzyć widok hierarchii tuż tuż. Mój problem polega na tym, że nie mogę zrozumieć, co to znaczy.

Jeśli utworzę RootViewControlleri wywołam addSubViewten kontroler, spodziewałbym się, że dodane widoki będą okablowane dla viewWillAppearzdarzeń.

Czy ktoś ma przykład złożonej hierarchii widoków programistycznych, która z powodzeniem odbiera viewWillAppearzdarzenia na każdym poziomie?

Stan Dokumentów Apple:

Ostrzeżenie: jeśli widok należący do kontrolera widoku zostanie dodany bezpośrednio do hierarchii widoków, kontroler widoku nie otrzyma tego komunikatu. Jeśli wstawisz lub dodasz widok do hierarchii widoków i ma on kontroler widoku, powinieneś wysłać tę wiadomość bezpośrednio do skojarzonego kontrolera widoku. Jeśli nie wyślesz kontrolera widoku, ta wiadomość uniemożliwi wyświetlenie skojarzonej animacji.

Problem w tym, że nie opisują, jak to zrobić. Co oznacza „bezpośrednio”? Jak „pośrednio” dodajesz widok?

Jestem całkiem nowy w Cocoa i iPhonie, więc byłoby miło, gdyby oprócz podstawowego bzdura z Hello World były przydatne przykłady od Apple.

chzk
źródło
Miałem ten problem, dopóki nie zdałem sobie sprawy, że ogólnie źle rozumiałem zamierzone użycie podklas UIViewController. Sprawdź to pytanie. stackoverflow.com/questions/5691226/…
averydev
7
Uwaga !!! To już nie prawda na iOS 5 !!! Połączenia viewWillAppear and viewDidAppear automatycznie
Vilém Kurz

Odpowiedzi:

55

Jeśli używasz kontrolera nawigacji i ustawisz jego delegata, metody widoku {Will, Did} {Appear, Disappear} nie zostaną wywołane.

Zamiast tego należy użyć metod delegata kontrolera nawigacji:

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
mmalc
źródło
2
Nie ustawiłem delegata kontrolera nawigacji, a metoda nadal nie była wywoływana. W każdym razie ustawiłem to, a następnie użyłem metod, o których wspomniałeś powyżej. Dzięki.
Dimitris
Widzę to samo co Dimitris
jkp
7
Właśnie przetestowałem to w iOS4 i iOS5: To NIE jest prawda: ustawienie delegata kontrolera nawigacji, a następnie przesłanie do niego widoku spowoduje uruchomienie viewWillAppear: itd.
DaGaMs
Swift 3: func navigationController (_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {} AND func navigationController (_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {}
Nalo
28

Napotkałem ten sam problem. Po prostu wyślij viewWillAppearwiadomość do kontrolera widoku, zanim dodasz go jako widok podrzędny. (Jest jeden parametr BOOL, który informuje kontroler widoku, czy animacja ma się pojawić, czy nie).

[myViewController viewWillAppear:NO];

Spójrz na RootViewController.m w przykładzie metronomu.

(Właściwie uważam, że przykładowe projekty Apple'a są świetne. Jest o wiele więcej niż HelloWorld;)

lajos
źródło
3
Właściwie powinieneś wywołać viewWillAppear po dodaniu go do widoku podrzędnego. W przeciwnym razie IBOutlets / IBActions nie zostaną połączone.
4thSpace
2
Tak, później. Utworzono widok podrzędny z XIB, a viewWillAppear nie został wywołany. Nazwij to sam i wszystko działa dobrze.
JOM
Dziękuję Ci! To było dokładnie to dla mnie. Dodawałem ręcznie widok podrzędny przez [scrollView addSubview:controller.view];. [controller viewWillAppear:NO];Później dodałem linię i voila! Działał jak urok.
Rob S.
Najprawdopodobniej dzieje się tak dlatego, że UIViewController kontroluje widok, który jest podwidokiem widoku kontrolowanego przez inny UIViewController. Nie jest to zamierzony wzorzec projektowy. Więcej wyjaśnień znajdziesz w tym poście. stackoverflow.com/questions/5691226/…
averydev
6
Uwaga !!! To już nie prawda na iOS 5 !!! Wywołuje automatycznie viewWillAppear i viewDidAppear. Jeśli zadzwonisz ręcznie, zostanie wywołany dwukrotnie.
Vilém Kurz
18

W końcu znalazłem rozwiązanie, które DZIAŁA!

UINavigationControllerDelegate

Myślę, że sednem tego jest ustawienie delegata kontrolki nawigacji na kontroler widoku, w którym się znajduje, i zaimplementowanie UINavigationControllerDelegatedwóch metod. Znakomity! Jestem tak podekscytowany, że w końcu znalazłem rozwiązanie!

Chris
źródło
jak przypisać rootviewcontroller jako delegata dla navigationcontroller?
Gargo,
1
NIE DZIAŁA! spróbuj zminimalizować aplikację i zmaksymalizować ją
Vyachaslav Gerchicov
8

Po prostu miałem ten sam problem. W mojej aplikacji mam 2 kontrolery nawigacyjne i wciskanie tego samego kontrolera widoku w każdym z nich działało w jednym przypadku a nie w drugim. Chodzi mi o to, że po naciśnięciu tego samego kontrolera widoku w pierwszym UINavigationController, viewWillAppearzostał wywołany, ale nie po naciśnięciu drugiego kontrolera nawigacyjnego.

Potem natknąłem się na ten post UINavigationController powinien wywołać metody viewWillAppear / viewWillDisappear

I zdałem sobie sprawę, że mój drugi kontroler nawigacyjny przedefiniował viewWillAppear. Sprawdzanie kodu pokazało, że nie dzwonię

[super viewWillAppear:animated];

Dodałem i zadziałało!

Dokumentacja mówi:

Jeśli zmienisz tę metodę, musisz wywołać super w pewnym momencie swojej implementacji.

Antoine
źródło
To samo tutaj. Zepsuty, nie dzwoniąc do super.
olivaresF
5

Używałem kontrolera nawigacyjnego. Kiedy chcę zejść do innego poziomu danych lub pokazać mój widok niestandardowy, używam następujących opcji:

[self.navigationController pushViewController:<view> animated:<BOOL>];

Kiedy to robię, viewWillAppearfunkcja uruchamia się. Przypuszczam, że to kwalifikuje się jako „pośrednie”, ponieważ sam nie wywołuję właściwej addSubViewmetody. Nie wiem, czy ma to zastosowanie w 100% do Twojej aplikacji, ponieważ nie mogę stwierdzić, czy używasz kontrolera nawigacyjnego, ale może to da wskazówkę.

Josh Gagnon
źródło
5

Dzięki iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppearA ViewDidAppearnie zostanie wywołana w prezentowaniu kontroler widoku na iOS 13, który wykorzystuje nową prezentację modalne że nie obejmuje cały ekran.

Kredyty idą do Arka Holko . Naprawdę uratował mi dzień.

wprowadź opis obrazu tutaj

BilalReffas
źródło
4

Po pierwsze, pasek zakładek powinien znajdować się na poziomie głównym, tj. Być dodany do okna, zgodnie z dokumentacją Apple. Ma to kluczowe znaczenie dla prawidłowego zachowania.

Po drugie, można użyć UITabBarDelegate/ UINavigationBarDelegatedo przekazania powiadomień ręcznie, ale okazało się, że aby dostać całą hierarchię widzenia połączeń działała poprawnie, wszystko, co musiałem zrobić, to połączenie ręcznie

[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];

i

[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];

.. TYLKO RAZ przed skonfigurowaniem kontrolerów widoku na odpowiednim kontrolerze (zaraz po przypisaniu). Odtąd poprawnie wywoływał te metody na swoich kontrolerach widoku podrzędnego.

Moja hierarchia wygląda tak:

window
    UITabBarController (subclass of)
        UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
            UINavigationController (subclass of)
                UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods

Już samo wywołanie wspomnianych metod na kontrolerze tab / nav po raz pierwszy zapewniło poprawne przekazanie WSZYSTKICH zdarzeń. Powstrzymało mnie to od ręcznego dzwonienia do nich zUINavigationBarDelegate / UITabBarControllerDelegateMethods.

Uwaga dodatkowa: Co ciekawe, kiedy to nie działało, metoda prywatna

- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController 

.. który możesz zobaczyć ze stosu wywołań na działającej implementacji, zwykle wywołuje viewWill/Did..metody, ale tak się nie dzieje, dopóki nie wykonałem powyższego (mimo że został wywołany).

Myślę, że BARDZO ważne jest, aby plik UITabBarController był na poziomie okna, a dokumenty wydają się to potwierdzać.

Mam nadzieję, że to było jasne (ish), chętnie odpowiem na dalsze pytania.

Sam
źródło
3

Ponieważ żadna odpowiedź nie jest akceptowana, a ludzie (tak jak ja) lądują tutaj, podaję moją odmianę. Chociaż nie jestem pewien, czy to był pierwotny problem. Kiedy kontroler nawigacji jest dodawany jako podwidok do innego widoku, musisz sam wywołać metody viewWillAppear / Dissappear itp. W następujący sposób:

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [subNavCntlr viewWillAppear:animated];
}

- (void) viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [subNavCntlr viewWillDisappear:animated];
}

Żeby przykład był kompletny. Ten kod pojawia się w moim ViewController, w którym utworzyłem i dodałem kontroler nawigacji do widoku, który umieściłem w widoku.

- (void)viewDidLoad {

    // This is the root View Controller
    rootTable *rootTableController = [[rootTable alloc]
                 initWithStyle:UITableViewStyleGrouped];

    subNavCntlr = [[UINavigationController alloc]   
                  initWithRootViewController:rootTableController];

    [rootTableController release];

    subNavCntlr.view.frame = subNavContainer.bounds;

    [subNavContainer addSubview:subNavCntlr.view];

    [super viewDidLoad];
}

.h wygląda tak

@interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
    IBOutlet UIView *subNavContainer;
    UINavigationController *subNavCntlr;
}

@end

W pliku nib mam widok, a poniżej tego widoku mam etykietę obraz i kontener (inny widok), w którym umieszczam kontroler. Oto jak to wygląda. Musiałem wymieszać kilka rzeczy, ponieważ to była praca dla klienta.

tekst alternatywny

hol
źródło
3

Widoki są dodawane „bezpośrednio”, dzwoniąc [view addSubview:subview] . Widoki są dodawane „pośrednio” za pomocą takich metod, jak paski kart lub paski nawigacji, które zamieniają widoki podrzędne.

Za każdym razem, gdy dzwonisz [view addSubview:subviewController.view], powinieneś zadzwonić[subviewController viewWillAppear:NO] (lub TAK, w zależności od przypadku).

Miałem ten problem, kiedy zaimplementowałem własny system zarządzania widokiem roota dla podekranu w grze. Ręczne dodanie wywołania do viewWillAppear wyleczyło mój problem.

AndrewS
źródło
3

Poprawnym sposobem na to jest użycie interfejsu API zawierającego UIViewController.

- (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
     UIViewController *viewController = ...;
     [self addChildViewController:viewController];
     [self.view addSubview:viewController.view];
     [viewController didMoveToParentViewController:self];
}
Hari Kunwar
źródło
To absolutnie poprawne nowoczesne rozwiązanie (iOS 9,8,7). Ponadto, jeśli zmieniasz osadzony kontroler widoku w locie, będziesz musiał wywołać [viewController willMoveToParentViewController: nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController];
Eli Burke,
1
W tym przypadku viewWillAppear:nadal nie można nazwać
Vyachaslav Gerchicov
2

Używam tego kodu dla kontrolerów widoku push i pop:

Pchać:

[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];

Muzyka pop:

[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];

.. i dla mnie działa dobrze.

Arash Zeinoddini
źródło
2

Bardzo częsty błąd jest następujący. Masz jeden widok, UIView* ai jeszcze jeden, UIView* b. Dodajesz b do a jako widok podrzędny. Jeśli spróbujesz wywołać viewWillAppear w b, to nigdy nie zostanie on uruchomiony, ponieważ jest to podwidok

giuseppe
źródło
2

iOS 13 wbił moją aplikację w tyłek tutaj. Jeśli zauważyłeś zmianę w zachowaniu od iOS 13, po prostu ustaw następujące ustawienia przed wysłaniem:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

Konieczne może być również ustawienie go w .storyboard w Inspektorze atrybutów (ustaw prezentację na pełny ekran).

Dzięki temu Twoja aplikacja będzie działać tak, jak we wcześniejszych wersjach systemu iOS.

Dollardime
źródło
1

Nie jestem tego w 100% pewien, ale myślę, że dodanie widoku do hierarchii widoków bezpośrednio oznacza wywołanie -addSubview:widoku kontrolera widoku (np. [viewController.view addSubview:anotherViewController.view]) Zamiast wypychania nowego kontrolera widoku na stos kontrolera nawigacji.

Martin Gordon
źródło
1

Myślę, że dodanie widoku podrzędnego niekoniecznie oznacza, że ​​widok się pojawi, więc nie ma automatycznego wywołania metody klasy, która będzie


źródło
1

Myślę, że to, co mają na myśli „bezpośrednio”, to podłączanie rzeczy w taki sam sposób, jak robi to szablon xcode „Navigation Application”, który ustawia UINavigationController jako jedyny podwidok okna UIWindow aplikacji.

Korzystanie z tego szablonu jest jedynym sposobem, w jaki mogłem uzyskać metody Will / Did / Appear / Disappear wywoływane w obiekcie ViewControllers po wypychaniu / wyskakiwaniu tych kontrolerów w UINavigationController. Żadne z innych rozwiązań w odpowiedziach tutaj nie zadziałało, w tym wdrożenie ich w RootController i przekazanie ich do (podrzędnego) NavigationController. Te funkcje (będą / pojawiały się / znikały) były wywoływane w moim RootController dopiero po wyświetleniu / ukryciu VC najwyższego poziomu, moich "loginów" i nawigacji VC, a nie sub-VC w kontrolerze nawigacji, więc nie miałem możliwości „przepuść je” do Nav VC.

Skończyło się na tym, że skorzystałem z funkcji delegata UINavigationControllera, aby wyszukać konkretne przejścia, które wymagały dalszych funkcji w mojej aplikacji, i to działa, ale wymaga trochę więcej pracy, aby „zasymulować” zniknięcie i pojawienie się funkcjonalności.

Również z zasady jest sprawą, aby zadziałał po tym, jak godzinami walę się dziś głową w ten problem. Wszelkie działające fragmenty kodu korzystające z niestandardowego RootController i podrzędnego VC nawigacji byłyby bardzo mile widziane.

Bogatyr
źródło
1

Na wypadek, gdyby to komuś pomogło. Miałem podobny problem, gdy mój ViewWillAppearnie odpala na UITableViewController. Po wielu zabawach zdałem sobie sprawę, że problem polegał na tym, UINavigationControllerże to, co kontroluje, UITableViewnie jest w widoku głównym. Kiedy to naprawię, teraz działa jak mistrz.

Andrzej
źródło
Czy możesz powiedzieć, „jak” to zrobiłeś?
Brabbeldas
1

Po prostu miałem ten problem i zajęło mi to 3 pełne godziny (z czego 2 googlowanie), aby go naprawić.

Pomocne okazało się po prostu usunięcie aplikacji z urządzenia / symulatora, wyczyszczenie i ponowne uruchomienie .

Mam nadzieję, że to pomoże

Finn Gaida
źródło
1
[self.navigationController setDelegate:self];

Ustaw delegata na główny kontroler widoku.

Gaurav
źródło
1

Dla Swift. Najpierw utwórz protokół, aby wywołać to, co chcesz wywołać w viewWillAppear

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Po drugie, utwórz klasę

class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
    if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
        updatedCntllr.myViewWillAppear()
    }
}

}

Po trzecie, ustaw wystąpienie ForceUpdateOnViewAppear jako element członkowski odpowiedniej klasy, która ma dostęp do kontrolera nawigacji i istnieje tak długo, jak istnieje kontroler nawigacji. Może to być na przykład główny kontroler widoku kontrolera nawigacji lub klasa, która go tworzy lub przedstawia. Następnie przypisz wystąpienie ForceUpdateOnViewAppear do właściwości delegata kontrolera nawigacji tak wcześnie, jak to możliwe.

Vadim Motorine
źródło
0

W moim przypadku problem był z niestandardową animacją przejścia. Po ustawieniumodalPresentationStyle = .custom viewWillAppear nie wywołane

w niestandardowej klasie animacji przejścia potrzebne są metody wywołania: beginAppearanceTransitioniendAppearanceTransition

ober
źródło
0

W moim przypadku był to po prostu dziwny błąd w emulatorze iOS 12.1. Zniknął po uruchomieniu na prawdziwym urządzeniu.

coldembrace
źródło
0

Stworzyłem klasę, która rozwiązuje ten problem. Po prostu ustaw go jako delegata kontrolera nawigacji i zaimplementuj prostą jedną lub dwie metody w kontrolerze widoku - które zostaną wywołane, gdy widok ma zostać wyświetlony lub został wyświetlony za pośrednictwem NavigationController

Oto GIST pokazujący kod

GregJaskiewicz
źródło
0

ViewWillAppear jest metodą nadpisywania klasy UIViewController, więc dodanie subView nie wywoła metody viewWillAppear, ale podczas prezentacji, push, pop, show, setFront lub popToRootViewController z elementu viewController zostanie wywołane viewWillAppear dla prezentowanego elementu viewController.

Abu Ul Hassan
źródło
0

Mój problem polegał na tym, że viewWillAppear nie był wywoływany podczas wychodzenia z kolejki. Odpowiedzią było wywołanie viewWillAppear (true) w rozwiniętym segue w kontrolerze widoku, do którego wracasz

@IBAction func unind (for relaxSegue: UIStoryboardSegue, ViewController FurtherVC: Any) {

   viewWillAppear(true)
}
user2385491
źródło
-2

Nie jestem pewien, czy to ten sam problem, który rozwiązałem.
W niektórych przypadkach metoda nie jest wykonywana w normalny sposób, na przykład „[self methodOne]”.

Próbować

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}
Sean
źródło
problem viewWillAppearnie jest w ogóle wywołany
Vyachaslav Gerchicov
-3

W dowolnym momencie powinien być aktywny tylko 1 kontroler UIViewController. Wszelkie podziały, którymi chcesz manipulować, powinny być dokładnie takie - subVIEWS - czyli UIView.

Używam prostej techniki do zarządzania moją hierarchią widoków i nie napotkałem jeszcze problemu, odkąd zacząłem robić rzeczy w ten sposób. Istnieją 2 kluczowe punkty:

  • do zarządzania „wartością ekranu” Twojej aplikacji należy użyć jednego kontrolera UIViewController
  • użyj UINavigationController do zmiany widoków

Co rozumiem przez „wartość ekranu”? Celowo jest trochę niejasny, ale generalnie jest to funkcja lub sekcja Twojej aplikacji. Jeśli masz kilka ekranów z tym samym obrazem tła, ale różnymi nakładkami / wyskakującymi okienkami itp., Powinien to być 1 kontroler widoku i kilka widoków podrzędnych. Nigdy nie powinieneś pracować z dwoma kontrolerami widoku. Należy zauważyć, że nadal można utworzyć wystąpienie UIView w jednym kontrolerze widoku i dodać go jako widok podrzędny innego kontrolera widoku, jeśli chcesz, aby określone obszary ekranu były wyświetlane w wielu kontrolerach widoku.

Jeśli chodzi o UINavigationController - to twój najlepszy przyjaciel! Wyłącz pasek nawigacji i określ NIE dla animacji, a będziesz mieć doskonały sposób przełączania ekranów na żądanie. Możesz wypchnąć i wyskoczyć kontrolery widoku, jeśli są w hierarchii, lub możesz przygotować tablicę kontrolerów widoku (w tym tablicę zawierającą pojedynczy VC) i ustawić ją jako stos widoku przy użyciu setViewControllers. Daje to całkowitą swobodę zmiany VC, jednocześnie zyskując wszystkie zalety pracy w oczekiwanym modelu Apple i poprawnego uruchamiania wszystkich wydarzeń itp.

Oto, co robię za każdym razem, gdy uruchamiam aplikację:

  • zacznij od aplikacji opartej na oknie
  • dodaj UINavigationController jako rootViewController okna
  • dodaj wszystko, co chcę, aby mój pierwszy UIViewController był jako rootViewController kontrolera nawigacyjnego

(uwaga zaczynająca się od okna to tylko osobiste preferencje - lubię konstruować rzeczy samodzielnie, więc wiem dokładnie, jak są budowane. Powinno działać dobrze z szablonem opartym na widoku)

Wszystkie zdarzenia strzelają poprawnie i w zasadzie życie jest dobre. Następnie możesz poświęcić cały swój czas na pisanie ważnych fragmentów aplikacji i nie martwić się próbami ręcznego zhakowania hierarchii widoków do kształtu.

Nigel Flack
źródło
Nie ma nic złego w posiadaniu aktywnych wielu kontrolerów widoku; UIViewController ma metody umożliwiające konstruowanie hierarchii (np. [AddChildViewController:]).
Richard,
1
Tak, teraz. W 2011 tak się nie stało. Odpowiedź była wtedy dokładna, trzeba przyznać, że teraz nie jest.
Nigel Flack,