Почему viewWillAppear не вызывается, когда приложение возвращается из фонового режима?


Я пишу приложение, и мне нужно, чтобы изменить мнение, если пользователь смотрит на приложение во время разговора по телефону.

я реализовал следующий метод:

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

но это не вызывается, когда приложение возвращается на передний план.

Я знаю, что могу реализовать:

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

но я не хочу этого делать. Я бы предпочел поместить всю информацию о макете в метод viewWillAppear: и пусть это обрабатывает все возможные вариант развития событий.

Я даже пытался вызвать viewWillAppear: from applicationWillEnterForeground:, но я не могу точно определить, какой текущий контроллер вида в этой точке.

кто-нибудь знает правильный способ справиться с этим? Я уверен, что мне не хватает очевидного решения.

5 231

5 ответов:

метод viewWillAppear следует принимать в контексте того, что происходит в вашем собственном приложении, а не в контексте вашего приложения, помещаемого на передний план, когда вы переключаетесь на него из другого приложения.

другими словами, если кто-то смотрит на другое приложение или принимает телефонный звонок, а затем переключается обратно на ваше приложение, которое было ранее на фоновом режиме, ваш UIViewController, который уже был виден, когда вы покинули свое приложение, "не заботится", так сказать-насколько он обеспокоен, он никогда не исчезал, и он все еще виден - и поэтому viewWillAppear не называется.

Я рекомендую не называть viewWillAppear себя-это имеет определенный смысл, который нельзя разрушить! Рефакторинг, который вы можете сделать для достижения того же эффекта, может быть следующим:

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

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

тогда и вы запускаете doMyLayoutStuff из соответствующего уведомления:

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

нет никакого способа из коробки, чтобы сказать, что является "текущим" UIViewController путь. Но вы можете найти способы обойти это, например, существуют методы делегирования UINavigationController для выяснения, когда в нем представлен UIViewController. Вы можете использовать такую вещь, чтобы отслеживать последний UIViewController, который был представлен.

обновление

если вы раскладываете UIs с соответствующими масками авторазбора на различных битах, иногда вам даже не нужно иметь дело с "ручной" выкладкой вашего пользовательского интерфейса - он просто получает дело с...

Свифт

короткий ответ:

использовать NotificationCenter наблюдатель, а не 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
}

ответ

чтобы узнать, когда приложение возвращается из фона, использовать NotificationCenter наблюдатель, а не viewWillAppear. Вот пример проекта, который показывает, какие события происходят при. (Это адаптация эта цель-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")
    }

}

на первый запуск приложения, порядок вывода:

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

после нажатия кнопки home, а затем вернуть приложение на передний план, порядок вывода:

will enter foreground
did become active 

так что если вы изначально пытаетесь использовать viewWillAppear затем UIApplicationWillEnterForeground - это, наверное, то, что вы хотите.

Примечание

начиная с iOS 9 и более поздних версий, вам не нужно удалять наблюдателя. Элемент документация гласит:

если ваше приложение предназначено для iOS 9.0 и позже или macOS 10.11 и позже, вы не нужно отменять регистрацию наблюдателя в его dealloc метод.

используйте Центр уведомлений в viewDidLoad: метод вашего ViewController для вызова метода и оттуда делать то, что вы должны были сделать в вашем viewWillAppear: метод. Звоню viewWillAppear: непосредственно не является хорошим вариантом.

- (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");
}

viewWillAppear:animated:, один из самых запутанных методов в SDK iOS, на мой взгляд, никогда не вызывается в такой ситуации, т. е. переключение приложений. Этот метод вызывается только в соответствии с отношением между представлением контроллера вида и окно приложения, т. е. сообщение отправляется контроллеру вида только в том случае, если его вид отображается в окне приложения, а не на экране.

когда ваше приложение идет фон, очевидно, самые верхние виды окна приложения не видны пользователю. Однако в перспективе окна приложения они по-прежнему являются самыми верхними представлениями, и поэтому они не исчезли из окна. Скорее, эти представления исчезли, потому что окно приложения исчезло. Они не исчезли, потому что они исчезли С окне.

поэтому, когда пользователь переключается обратно в приложение, они, очевидно, появляются на экране, потому что снова появится окно. Но с точки зрения окна они вовсе не исчезли. Поэтому контроллеры представления никогда не получают viewWillAppear:animated сообщение.

просто пытаюсь сделать это как можно проще см. код ниже:

- (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");


}