为什么当应用从后台返回时,viewWillAppear不会被调用?
为什么当应用从后台返回时,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:方法中,并让它处理所有可能的情况。
我甚至尝试从applicationWillEnterForeground:中调用viewWillAppear:,但似乎无法确定在该点上哪个是当前的视图控制器。
有人知道处理这种情况的正确方法吗?我肯定我错过了一个明显的解决方案。
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。
更新
如果你使用了适当的自动调整大小掩码布局UI,有时你甚至不需要处理你的UI的"手动"布局--它会自动处理...
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
在按下Home键然后再把应用程序带回前台后,输出的顺序是:
will enter foreground did become active
所以,如果您最初尝试使用viewWillAppear
,那么UIApplication.willEnterForegroundNotification
可能是您想要的。
注
从iOS 9及以上版本开始,你不需要删除观察者。文档中指出:
如果你的应用程序目标为iOS 9或更高版本或macOS 10.11或更高版本,则无需在其
dealloc
方法中注销观察者。