ios 当应用程序从后台返回时,为什么不调用 viewWillAppear?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5277940/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:12:21  来源:igfitidea点击:

Why does viewWillAppear not get called when an app comes back from the background?

iosobjective-ciphoneviewwillappear

提问by Philip Walton

I'm writing an app and I need to change the view if the user is looking at the app while talking on the phone.

我正在编写一个应用程序,如果用户在打电话时正在查看应用程序,我需要更改视图。

I've implemented the following method:

我已经实现了以下方法:

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

But it's not being called when the app returns to the foreground.

但是当应用程序返回到前台时它不会被调用。

I know that I can implement:

我知道我可以实施:

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

but I don't want to do this. I'd much rather put all my layout information in the viewWillAppear: method, and let that handle all possible scenarios.

但我不想这样做。我更愿意把我所有的布局信息放在 viewWillAppear: 方法中,让它处理所有可能的场景。

I've even tried to call viewWillAppear: from applicationWillEnterForeground:, but I can't seem to pinpoint which is the current view controller at that point.

我什至试图从 applicationWillEnterForeground: 调用 viewWillAppear:,但我似乎无法确定当时哪个是当前的视图控制器。

Does anybody know the proper way to deal with this? I'm sure I'm missing an obvious solution.

有人知道处理这个问题的正确方法吗?我确定我错过了一个明显的解决方案。

采纳答案by occulus

The method viewWillAppearshould be taken in the context of what is going on in your own application, and not in the context of your application being placed in the foreground when you switch back to it from another app.

该方法viewWillAppear应采取什么是你自己的应用程序正在进行,而不是在你的应用程序的情况下被放置在前台当你从另一个应用程序切换回它的上下文。

In other words, if someone looks at another application or takes a phone call, then switches back to your app which was earlier on backgrounded, your UIViewController which was already visible when you left your app 'doesn't care' so to speak -- as far as it is concerned, it's never disappeared and it's still visible -- and so viewWillAppearisn't called.

换句话说,如果有人查看另一个应用程序或接听电话,然后切换回您的应用程序,该应用程序在后台运行较早,您的 UIViewController 在您离开应用程序时已经可见,“不在乎”可以这么说——就它而言,它从未消失过,而且仍然可见——因此viewWillAppear没有被调用。

I recommend against calling the viewWillAppearyourself -- it has a specific meaning which you shouldn't subvert! A refactoring you can do to achieve the same effect might be as follows:

我建议不要调用viewWillAppear你自己——它有一个你不应该颠覆的特定含义!您可以为实现相同效果而进行的重构可能如下所示:

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

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

Then also you trigger doMyLayoutStufffrom the appropriate notification:

然后你也doMyLayoutStuff从适当的通知触发:

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

There's no out of the box way to tell which is the 'current' UIViewController by the way. But you can find ways around that, e.g. there are delegate methods of UINavigationController for finding out when a UIViewController is presented therein. You could use such a thing to track the latest UIViewController which has been presented.

顺便说一下,没有开箱即用的方法来判断哪个是“当前” UIViewController 。但是您可以找到解决方法,例如,有 UINavigationController 的委托方法用于找出 UIViewController 何时出现在其中。您可以使用这样的东西来跟踪已呈现的最新 UIViewController。

Update

更新

If you layout out UIs with the appropriate autoresizing masks on the various bits, sometimes you don't even need to deal with the 'manual' laying out of your UI - it just gets dealt with...

如果您在各个位上使用适当的自动调整大小掩码来布局 UI,有时您甚至不需要处理 UI 中的“手动”布局——它只是被处理......

回答by Suragch

Swift

迅速

Short answer

简答

Use a NotificationCenterobserver rather than viewWillAppear.

使用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
}


Long answer

长答案

To find out when an app comes back from the background, use a NotificationCenterobserver rather than viewWillAppear. Here is a sample project that shows which events happen when. (This is an adaptation of this Objective-C answer.)

要了解应用程序何时从后台返回,请使用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")
    }

}

On first starting the app, the output order is:

首次启动应用程序时,输出顺序为:

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

After pushing the home button and then bringing the app back to the foreground, the output order is:

按下home键再将app带回前台后,输出顺序为:

will enter foreground
did become active 

So if you were originally trying to use viewWillAppearthen UIApplication.willEnterForegroundNotificationis probably what you want.

因此,如果您最初尝试使用,viewWillAppear那么UIApplication.willEnterForegroundNotification可能就是您想要的。

Note

笔记

As of iOS 9 and later, you don't need to remove the observer. The documentationstates:

从 iOS 9 及更高版本开始,您无需移除观察者。该文件指出:

If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its deallocmethod.

如果您的应用面向 iOS 9.0 及更高版本或 macOS 10.11 及更高版本,则无需在其dealloc方法中取消注册观察者。

回答by Manju

Use Notification Center in the viewDidLoad:method of your ViewController to call a method and from there do what you were supposed to do in your viewWillAppear:method. Calling viewWillAppear:directly is not a good option.

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

回答by MHC

viewWillAppear:animated:, one of the most confusing methods in the iOS SDKs in my opinion, is never be invoked in such a situation, i.e., application switching. That method is only invoked according to the relationship between the view controller's view and the application's window, i.e., the message is sent to a view controller only if its view appears on the application's window, not on the screen.

viewWillAppear:animated:,在我看来,iOS SDK 中最令人困惑的方法之一,永远不会在这种情况下被调用,即应用程序切换。该方法仅根据视图控制器的视图和应用程序的窗口之间的关系被调用,即,只有当视图控制器的视图出现在应用程序的窗口而不是屏幕上时,才会将消息发送到视图控制器。

When your application goes background, obviously the topmost views of the application window are no longer visible to the user. In your application window's perspective, however, they are still the topmost views and therefore they did not disappear from the window. Rather, those views disappeared because the application window disappeared. They did not disappeared because they disappeared fromthe window.

当您的应用程序进入后台时,应用程序窗口的最顶层视图显然不再对用户可见。然而,从应用程序窗口的角度来看,它们仍然是最顶层的视图,因此它们并没有从窗口中消失。相反,这些视图消失是因为应用程序窗口消失了。他们没有消失,因为他们窗户消失了。

Therefore, when the user switches back to your application, they obviously seem to appear on the screen, because the window appears again. But from the window's perspective, they haven't disappeared at all. Therefore the view controllers never get the viewWillAppear:animatedmessage.

因此,当用户切换回您的应用程序时,它们显然似乎出现在屏幕上,因为窗口再次出现。但从窗户的角度来看,它们并没有完全消失。因此视图控制器永远不会收到viewWillAppear:animated消息。

回答by ConfusedDeer

Just trying to make it as easy as possible see code below:

只是想让它尽可能简单,请参阅下面的代码:

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


}

回答by aviran

Swift 4.2 / 5

斯威夫特 4.2 / 5

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground),
                                           name: Notification.Name.UIApplicationWillEnterForeground,
                                           object: nil)
}

@objc func willEnterForeground() {
   // do what's needed
}