it-swarm.com.ru

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

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

259
Philip Walton

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

Обновление

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

190
occulus

Swift

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

Используйте 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.

166
Suragch

Используйте Центр уведомлений в методе 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");
}
138
Manju

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

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

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

33
MHC

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

- (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
ConfusedDeer