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

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

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

- (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: из applicationWillEnterForeground :, но я не могуточно, который является текущим контроллером представления в этой точке.

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

Ответы [ 5 ]

190 голосов
/ 11 марта 2011

Метод 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, который был представлен.

Обновление

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

169 голосов
/ 30 декабря 2015

Свифт

Краткий ответ

Используйте NotificationCenter наблюдателя вместо viewWillAppear.

override func viewDidLoad() {
    super.viewDidLoad()

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

}

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

Длинный ответ

Чтобы узнать, когда приложение возвращается из фона, используйте NotificationCenter наблюдатель, а не viewWillAppear.Вот пример проекта, который показывает, какие события происходят когда.(Это адаптация этого ответа 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: UIApplication.didBecomeActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, 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

После нажатияЕсли вы нажмете кнопку «Домой», а затем вернете приложение на передний план, порядок вывода будет таким:

will enter foreground
did become active 

Так что, если вы изначально пытались использовать viewWillAppear, то UIApplication.willEnterForegroundNotification, вероятно, то, что вам нужно.

Примечание

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

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

138 голосов
/ 28 августа 2013

Используйте Центр уведомлений в методе 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");
}
33 голосов
/ 11 марта 2011

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

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

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

3 голосов
/ 17 июля 2015

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

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


}
...