ViewDidAppear nie jest wywoływana podczas otwierania aplikacji w tle

175

Mam kontroler widoku, w którym moja wartość wynosi 0 (etykieta) i kiedy otwieram ten kontroler widoku z innego ViewController, viewDidAppearustawiłem wartość 20 na etykiecie. To działa dobrze, ale gdy zamknę aplikację i niż ponownie otwieram aplikację ale wartość nie zmienia się, ponieważ viewDidLoad, viewDidAppeari viewWillAppearnic się nazywa. Jak mogę zadzwonić po otwarciu mojej aplikacji. Czy muszę coś robić applicationDidBecomeActive?

Zohaib
źródło
Możesz opublikować lokalne powiadomienie, gdy aplikacja stanie się aktywna i dodać kontroler widoku jako obserwatora i zaktualizować wartości.
Adil Soomro,

Odpowiedzi:

314

Zaciekawiony dokładną sekwencją zdarzeń, przygotowałem aplikację w następujący sposób: (@Zohaib, możesz użyć poniższego kodu NSNotificationCenter, aby odpowiedzieć na swoje pytanie).

// AppDelegate.m

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    NSLog(@"app will enter foreground");
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSLog(@"app did become active");
}

// ViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"view did load");

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}

- (void)appDidBecomeActive:(NSNotification *)notification {
    NSLog(@"did become active notification");
}

- (void)appWillEnterForeground:(NSNotification *)notification {
    NSLog(@"will enter foreground notification");
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"view will appear");
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"view did appear");
}

Podczas uruchamiania dane wyjściowe wyglądają następująco:

2013-04-07 09:31:06.505 myapp[15459:11303] view did load
2013-04-07 09:31:06.507 myapp[15459:11303] view will appear
2013-04-07 09:31:06.511 myapp[15459:11303] app did become active
2013-04-07 09:31:06.512 myapp[15459:11303] did become active notification
2013-04-07 09:31:06.517 myapp[15459:11303] view did appear

Wprowadź tło, a następnie ponownie wprowadź pierwszy plan:

2013-04-07 09:32:05.923 myapp[15459:11303] app will enter foreground
2013-04-07 09:32:05.924 myapp[15459:11303] will enter foreground notification
2013-04-07 09:32:05.925 myapp[15459:11303] app did become active
2013-04-07 09:32:05.926 myapp[15459:11303] did become active notification
danh
źródło
1
Danh, zmapowałeś UIApplicationWillEnterForegroundNotification do appDidEnterForeground :. Czy to nie jest trochę mylące? Zwróć uwagę na „will” i „did”. Czy to było zamierzone?
Lubiluk
@Lubiluk - niezamierzone. Będę edytować. Dobry chwyt.
danh
4
To była bardzo przydatna odpowiedź. Zrobiłem wersję Swift go tutaj .
Suragch
Doskonała demonstracja trybu tła!
Marcelo dos Santos
Jaka jest sekwencja zdarzeń po dwukrotnym dotknięciu przycisku strony głównej i zamknięciu aplikacji?
Amjad Husseini,
134

Korzystanie z Objective-C

Należy zarejestrować UIApplicationWillEnterForegroundNotificationw ViewController„s viewDidLoadmetody i kiedy aplikacja wraca z tłem możesz robić, co chcesz robić w metodzie zarejestrowanego dla powiadomienia. ViewController„s viewWillAppear lub viewDidAppear nie zostanie wywołana, gdy aplikacja wraca z tła na pierwszy plan.

-(void)viewDidLoad{

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doYourStuff)

  name:UIApplicationWillEnterForegroundNotification object:nil];
}

-(void)doYourStuff{

   // do whatever you want to do when app comes back from background.
}

Nie zapomnij wyrejestrować powiadomienia, dla którego jesteś zarejestrowany.

-(void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Uwaga Po zgłoszeniu viewControllerna UIApplicationDidBecomeActiveNotificationczym metoda będzie wywoływana za każdym razem, gdy aplikacja staje się aktywna, nie zaleca się zarejestrować viewControllerdo tego zgłoszenia.

Korzystanie ze Swift

Aby dodać obserwatora, możesz użyć następującego kodu

 override func viewDidLoad() {
    super.viewDidLoad()

     NotificationCenter.default.addObserver(self, selector: "doYourStuff", name: UIApplication.willEnterForegroundNotification, object: nil)
 }

 func doYourStuff(){
     // your code
 }

Aby usunąć obserwatora, możesz użyć funkcji deinit of swift.

deinit {
    NotificationCenter.default.removeObserver(self)
}
nsgulliver
źródło
4
Tak jest :) czasami trudno znaleźć odpowiedzi :)
nsgulliver
@nsgulliver Czy muszę ręcznie wywoływać wyrejestrowanie powiadomienia - (void) dealloc {[[NSNotificationCenter defaultCenter] removeObserver: self]; }. Czy aplikacja zrobi to za mnie?
iOS
43

Wersja Swift 3.0 ++

W swoim viewDidLoad, zarejestruj się w centrum powiadomień, aby słuchać tego otwartego z działania w tle

NotificationCenter.default.addObserver(self, selector:#selector(doSomething), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
        

Następnie dodaj tę funkcję i wykonaj wymaganą akcję

func doSomething(){
    //...
}

Na koniec dodaj tę funkcję, aby wyczyścić obserwatora powiadomień, gdy kontroler widoku zostanie zniszczony.

deinit {
    NotificationCenter.default.removeObserver(self)
}
Fangming
źródło
Łatwe i proste rozwiązanie do obsługi powiadomień wewnątrz VC +1
Mo Zaatar
Nie mogę uwierzyć, że ta miła odpowiedź jest pomijana w tak wielu innych podobnych / zduplikowanych pytaniach SO.
Hugo Allexis Cardona
11

Swift 4.2. wersja

Zarejestruj się w NotificationCenter, viewDidLoadaby otrzymywać powiadomienia o powrocie aplikacji z tła

NotificationCenter.default.addObserver(self, selector: #selector(doSomething), name: UIApplication.willEnterForegroundNotification, object: nil)

Zaimplementuj metodę, która powinna zostać wywołana.

@objc private func doSomething() {
    // Do whatever you want, for example update your view.
}

Możesz usunąć obserwatora, gdy ViewControllerzostanie zniszczony. Jest to wymagane tylko poniżej iOS9 i macOS 10.11

deinit {
    NotificationCenter.default.removeObserver(self)
}
gebirgsbärbel
źródło
1
FYI, jestem prawie pewien, że nie musisz już więcej zawracać sobie głowy usuwaniem obserwatorów ...
Fattie
3

Wystarczy, że kontroler widoku zarejestruje się na UIApplicationWillEnterForegroundNotificationpowiadomienie i zareaguje odpowiednio.

andreagiavatto
źródło
Jak to zrobię? nazwałem mój viewController w applicationDidBecomeActive, ale. nakłada się na viewController czy dobrze to zrobić?
Zohaib,
2
Nie wywołuj swojego viewController w applicationDidBecomeActive (co i tak jest błędne, ponieważ jest wywoływane wiele razy). Zarejestruj się, aby otrzymać powiadomienie w viewDidLoadsugerowanym przez Ciebie @nsgulliver. Twoje viewDidAppearwezwanie również, doYourStuffaby ustawić etykietę z żądaną wartością.
andreagiavatto,
3

Myślę, że rejestracja w UIApplicationWillEnterForegroundNotification jest ryzykowna, ponieważ możesz skończyć z więcej niż jednym kontrolerem reagującym na to powiadomienie. Nic nie gwarantuje, że te kontrolery są nadal widoczne po otrzymaniu powiadomienia.

Oto, co robię: wymuszam wywołanie viewDidAppear na aktywnym kontrolerze bezpośrednio z metody didBecomeActive delegata aplikacji:

Dodaj poniższy kod do - (void)applicationDidBecomeActive:(UIApplication *)application

UIViewController *activeController = window.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
    activeController = [(UINavigationController*)window.rootViewController topViewController];
}
[activeController viewDidAppear:NO];
Erwan
źródło
7
Jest gwarantowane, jeśli kontroler wyrejestruje się (tak, jak powinien) dla UIApplicationWillEnterForegroundNotification w viewWillDisappear zamiast w dealloc. Wywołanie viewDidAppear wygląda dla mnie jak włamanie, łamie semantykę (osobisty pogląd) i może zmylić ludzi (z doświadczenia).
joakim
3

spróbuj dodać to w AppDelegate applicationWillEnterForeground.

func applicationWillEnterForeground(_ application: UIApplication) {        
    // makes viewWillAppear run
    self.window?.rootViewController?.beginAppearanceTransition(true, animated: false)
    self.window?.rootViewController?.endAppearanceTransition()
}
richc
źródło
2

Zgodnie z dokumentacją Apple:

(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated;

Opis:
informuje kontroler podrzędny, że jego wygląd ma się zmienić. Jeśli wdrażasz niestandardowy kontroler kontenera, użyj tej metody, aby poinformować dziecko, że jego widoki wkrótce się pojawią lub znikną . Nie wywoływać viewWillAppear:, viewWillDisappear:, viewDidAppear:, lub viewDidDisappear:bezpośrednio .

(void)endAppearanceTransition;

Opis:

Informuje kontroler podrzędny, że jego wygląd się zmienił. Jeśli implementujesz niestandardowy kontroler kontenera, użyj tej metody, aby poinformować dziecko, że przejście widoku zostało zakończone.

Przykładowy kod:

(void)applicationDidEnterBackground:(UIApplication *)application
{

    [self.window.rootViewController beginAppearanceTransition: NO animated: NO];  // I commented this line

    [self.window.rootViewController endAppearanceTransition]; // I commented this line

}

Pytanie: Jak to naprawiłem?

Odp : Znalazłem ten fragment linii w aplikacji. Te linie sprawiły, że moja aplikacja nie otrzymywała żadnych powiadomień ViewWillAppear. Kiedy skomentowałem te linie, działa dobrze .

Lakshmi
źródło