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

时间:2011-03-11 20:25:47

标签: ios objective-c iphone 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:但我似乎无法确定当前哪个是当前的视图控制器。

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

7 个答案:

答案 0 :(得分:190)

方法viewWillAppear应该在您自己的应用程序中发生的事情的上下文中进行,而不是在您从另一个应用程序切换回应用程序时将应用程序置于前台的上下文中。 / p>

换句话说,如果有人查看另一个应用程序或接听电话,然后切换回您之前在后台运行的应用程序,您的UIViewController在您离开应用程序时已经可见“不关心”所以说 - 就它而言,它永远不会消失,而且它仍然可见 - 所以viewWillAppear不被称为。{/ p>

我建议不要自己调用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中的“手册” - 它只是处理...

答案 1 :(得分:165)

夫特

简短回答

使用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。这是一个示例项目,显示何时发生的事件。 (这是this Objective-C answer的改编。)

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及更高版本开始,您无需删除观察者。 documentation州:

  

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

答案 2 :(得分:138)

在ViewController的viewDidLoad:方法中使用通知中心来调用方法,然后在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");
}

答案 3 :(得分:33)

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

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

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

答案 4 :(得分:3)

尽量让它变得尽可能简单,请参阅下面的代码:

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


}

答案 5 :(得分:2)

Swift 4.2 / 5

import java.util.*;

class ddr {
    //int n;

    static void hh(int n){
        if(n<1)  System.out.println("ffbdf");
        hh(n-1);
    }

    public static void main(String[] args) {
       Scanner in = new Scanner(System.in);
       int no = in.nextInt();

       ddr k = new ddr();
       hh(no);
       //hh(no);
    }

答案 6 :(得分:0)

使用 SwiftUI 更容易:

var body: some View {     
    Text("Hello World")
    .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
        print("Moving to background!")
    }
    .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
        print("Moving back to foreground!")
    }   
}