Dlaczego viewWillAppear nie jest wywoływany, gdy aplikacja wraca z tła?

Piszę aplikację i muszę zmienić widok, jeśli użytkownik patrzy na aplikację podczas rozmowy przez telefon.

Zaimplementowałem następującą metodę:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"viewWillAppear:");
    _sv.frame = CGRectMake(0.0, 0.0, 320.0, self.view.bounds.size.height);
}

Ale nie jest wywoływany, gdy aplikacja wraca na pierwszy plan.

Wiem, że mogę zaimplementować:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
Ale nie chcę tego robić. Wolałbym umieścić wszystkie moje informacje o układzie w metodzie viewWillAppear: i pozwolić, aby obsłużyła wszystkie możliwe scenariusze. Próbowałem nawet zadzwonić. viewWillAppear: from applicationWillEnterForeground:, ale nie mogę wskazać, który jest bieżącym kontrolerem widoku w tym momencie. Czy ktoś wie jak sobie z tym poradzić? Jestem pewien, że brakuje mi oczywistego rozwiązania.
Author: Philip Walton, 2011-03-11

5 answers

Metoda viewWillAppear powinna być brana w kontekście tego, co dzieje się we własnej aplikacji, a nie w kontekście umieszczania aplikacji na pierwszym planie, gdy przełączysz się z powrotem do niej z innej aplikacji.

Innymi słowy, jeśli ktoś patrzy na inną aplikację lub odbiera telefon, a następnie przełącza się z powrotem do aplikacji, która była wcześniej na backgrounded, Twój UIViewController, który był już widoczny po opuszczeniu aplikacji "nie obchodzi", że tak powiem - o ile jest / align = "left" /

Odradzam nazywanie siebie viewWillAppear - ma to konkretne znaczenie, którego nie należy obalać! Refaktoryzacja, którą możesz zrobić, aby osiągnąć ten sam efekt, może wyglądać następująco:

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

- (void)doMyLayoutStuff:(id)sender {
    // stuff
}

Następnie uruchamiasz doMyLayoutStuff z odpowiedniego powiadomienia:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doMyLayoutStuff:) name:UIApplicationDidChangeStatusBarFrameNotification object:self];

Nie ma wyjętego z pudełka sposobu, aby stwierdzić, który jest 'aktualnym' kontrolerem UIViewController. Ale można znaleźć sposoby wokół że, np. istnieją metody delegatów UINavigationController, aby dowiedzieć się, kiedy UIViewController jest w nim przedstawiony. Możesz użyć tego do śledzenia najnowszego Uiviewcontrollera, który został zaprezentowany.

Update

Jeśli układasz Interfejsy z odpowiednimi maskami automatyzacji na różnych bitach, czasami nawet nie musisz zajmować się "ręcznym" układaniem interfejsu - po prostu się z tym uporasz...

 176
Author: occulus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-11 14:51:28

Swift

Krótka odpowiedź

Użyj obserwatora NotificationCenter zamiast viewWillAppear.

override func viewDidLoad() {
    super.viewDidLoad()

    // set observer for UIApplicationWillEnterForeground
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
}

// my selector that was defined above
@objc func willEnterForeground() {
    // do stuff
}

Długa odpowiedź

Aby dowiedzieć się, kiedy aplikacja wraca z tła, użyj obserwatora NotificationCenter zamiast viewWillAppear. Oto przykładowy projekt, który pokazuje, które wydarzenia zdarzają się, kiedy. (Jest to adaptacja tej odpowiedzi Objective-C .)

import UIKit
class ViewController: UIViewController {

    // MARK: - Overrides

    override func viewDidLoad() {
        super.viewDidLoad()
        print("view did load")

        // add notification observers
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)

    }

    override func viewWillAppear(_ animated: Bool) {
        print("view will appear")
    }

    override func viewDidAppear(_ animated: Bool) {
        print("view did appear")
    }

    // MARK: - Notification oberserver methods

    @objc func didBecomeActive() {
        print("did become active")
    }

    @objc func willEnterForeground() {
        print("will enter foreground")
    }

}

Przy pierwszym uruchomieniu aplikacji, Kolejność wyjściowa wynosi:

view did load
view will appear
did become active
view did appear

Po wciśnięciu w tym celu należy użyć przycisku home, a następnie przywrócić aplikację na pierwszy plan, kolejność wyjściowa wynosi:

will enter foreground
did become active 

Więc jeśli początkowo próbowałeś użyć viewWillAppear to UIApplicationWillEnterForeground jest prawdopodobnie tym, czego chcesz.

Uwaga

[13]}od wersji iOS 9 i nowszych nie trzeba usuwać obserwatora. Dokumentacja stwierdza:

Jeśli Twoja aplikacja jest przeznaczona na systemy iOS 9.0 i nowsze lub macOS 10.11 i nowsze, nie trzeba wyrejestrować obserwatora w jego metodzie dealloc.

 142
Author: Suragch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-21 05:42:43

Użyj Centrum powiadomień w metodzie viewDidLoad: Twojego kontrolera Viewcontrollera, aby wywołać metodę i stamtąd zrobić to, co powinieneś zrobić w swojej metodzie viewWillAppear:. Bezpośrednie wywołanie viewWillAppear: nie jest dobrym rozwiązaniem.

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

    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector:@selector(applicationIsActive:) 
        name:UIApplicationDidBecomeActiveNotification 
        object:nil];

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

- (void)applicationIsActive:(NSNotification *)notification {
    NSLog(@"Application Did Become Active");
}

- (void)applicationEnteredForeground:(NSNotification *)notification {
    NSLog(@"Application Entered Foreground");
}
 137
Author: Manju,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-23 06:22:15

viewWillAppear:animated:, jedną z najbardziej mylących metod w zestawach SDK iOS moim zdaniem nigdy nie jest wywoływanie w takiej sytuacji, tj. przełączanie aplikacji. Metoda ta jest wywoływana tylko zgodnie z relacją pomiędzy widokiem kontrolera widoku a oknem aplikacji , tzn. wiadomość jest wysyłana do kontrolera widoku tylko wtedy, gdy jego widok pojawia się w oknie aplikacji, a nie na ekranie.

Gdy aplikacja przechodzi w tło, oczywiście najwyższe widoki aplikacji okna nie są już widoczne dla użytkownika. W perspektywie okna aplikacji są one jednak nadal najwyżej położonymi widokami i dlatego nie znikają z okna. Raczej te widoki zniknęły, ponieważ zniknęło okno aplikacji. Nie zniknęły, ponieważ zniknęły z Okna.

Dlatego, gdy użytkownik przełącza się z powrotem do aplikacji, wydaje się, że pojawiają się na ekranie, ponieważ okno pojawia się ponownie. Ale z z perspektywy okna, wcale nie zniknęły. Dlatego Kontrolery widoku nigdy nie otrzymują wiadomości viewWillAppear:animated.

 32
Author: MHC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-11 20:54:30

Po prostu staram się to jak najprostsze zobacz kod poniżej:

- (void)viewDidLoad
{
   [self appWillEnterForeground]; //register For Application Will enterForeground
}


- (id)appWillEnterForeground{ //Application will enter foreground.

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(allFunctions)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
    return self;
}


-(void) allFunctions{ //call any functions that need to be run when application will enter foreground 
    NSLog(@"calling all functions...application just came back from foreground");


}
 3
Author: ConfusedDeer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-23 06:26:46