简体   繁体   中英

refresh data after entering a foreground

After changing the default settings, I would like to refresh data of myViewController when I enter the foreground in AppDelegate. What I do is

AppDelegate.m

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    [window addSubview:[navigationController view]];
    NSLog(@"APPLICATION DID FINISH LAUNCHING");
    // listen for changes to our preferences when the Settings app does so,
    // when we are resumed from the backround, this will give us a chance to update our UI
    //
    [[NSNotificationCenter defaultCenter] addObserver:self
                                       selector:@selector(defaultsChanged:)
                                           name:NSUserDefaultsDidChangeNotification
                                         object:nil];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    /*
     Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
     */
    NSLog(@"APPLICATION WILL ENTER BACKGROUND");
    [myViewController viewDidLoad];
}

myViewController.m

- (void)viewDidLoad
{
    NSLog(@"VIEW DID LOAD IN MY VIEW CONTROLLER");
    // watch when the app has finished launching so we can update our preference settings and apply them to the UI
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSettings:) 
                                        name:UIApplicationDidFinishLaunchingNotification object:nil];
}

- (void)updateSettings:(NSNotification *)notif
{
    myLabel.text = @"data has just been modified";
}

However, there is nothing changed at all.

You have 2 problems in your code. First the sequence of the start-up functions (what is called Execution States) seems not clear to you, and second you added a listener to the function updateSettings but you never called it in the code above, and this is why nothing changed when your app starts.

Let me first explain the start-up sequence. When an app loads from a turned off device these states are fired:

application:didFinishLaunchingWithOptions:
applicationDidBecomeActive:

After that if you press the Home button these states are fired:

applicationWillResignActive:
applicationDidEnterBackground:

Then if you enter into the app again, the following will occur:

applicationWillEnterForeground:
applicationDidBecomeActive:

Notice that the loading state only occurs once at the first load (but not after you return from a Home press). Now for every view the function viewDidLoad will be called only one time which is the first time this view was called. If call this view again (after it has been loaded) then the function viewWillAppear will be called instead. So usually refreshing occurs in viewWillAppear function.

An important thing that I noticed in your code which is improper is the usage of the main delegate functions. In applicationWillEnterForeground you manually called viewDidLoad while you shouldn't do that as this function will be called automatically as I explained above. Also I see you are adding Notification centers that are not needed.

Now lets see the second problem in your code. You are adding a Notification Center for the function updateSettings in the viewDidLoad . Well the loading of this view will occur after the event UIApplicationDidFinishLaunchingNotification therefore in practice you never called the function updateSettings . Moreover since this function is a member of your class you don't need a Notification Center to call it. We usually use a Notification Center when we need to call a function from another class. Simply what you need to do is call this function directly from viewDidLoad as follows:

[self updateSettings]

And if you need to update after the home button is pressed call the function from viewWillAppear .

I hope this fast explanation helps you.

EDIT: to answer your comment below

If you have only one view (no navigation controllers...), after it appears the first time it will stay in the memory and it won't appear again (so this function isn't called). Here you should catch the event UIApplicationDidBecomeActiveNotification so do the following:

In the viewDidLoad add a Notification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSettings) name:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication]];

This will allow the function updateSettings to be called every time your app wakes. Also remember to remove this listener at the end by:

[[NSNotificationCenter defaultCenter] removeObserver:self];

You have many problems with your code.

confusion between Foreground and Background

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"APPLICATION WILL ENTER BACKGROUND");
    ...

Well, no, it will enter FOREGROUND, and is leaving BACKGROUND.

missing super call in viewDidLoad

- (void)viewDidLoad {
    ...

You shall add a [super viewDidLoad];

direct call to viewDidLoad

- (void)applicationWillEnterForeground:(UIApplication *)application {
    [myViewController viewDidLoad];
    ...

Well, no, do not call viewDidLoad yourself, as it is supposed to only be called once by the system. Super classes or sub classes may be incompatible with multiple calls.

unbalanced observers

- (void)viewDidLoad {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSettings:) name:UIApplicationDidFinishLaunchingNotification object:nil];
    ...

By calling viewDidLoad multiple times, you were actually registering multiple observers for the same event. You need to have in your code a symmetry with as many calls to removeObserver than addObserver (note that you can also remove multiple observers at the same time).

missing implementation?

[[NSNotificationCenter defaultCenter] addObserver:self
                                   selector:@selector(defaultsChanged:)
                                       name:NSUserDefaultsDidChangeNotification
                                     object:nil];

Well, we don't see your implementation of defaultsChanged: , so it's unclear what you were trying to achieve. Was it to set a BOOL to YES and subsequently check that value to determine if preferences were changed? Something like that?

- (void)defaultsChanged:(id)notif {
    self.refreshData = YES;
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    if (self.refreshData) {
        self.refreshData = NO;
        // we refresh data now
        ...
    }
}

Swift 5 Version to track when app becomes active

// subscribe to notification once
override func viewDidLoad() {
    super.viewDidLoad()
    subscribeToNotification()
}

// Actual subscription
private func subscribeToNotification() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(didBecomeActive),
        name: UIApplication.didBecomeActiveNotification,
        object: nil
    )
}

// Where's where the refreshing logic goes
@objc private func didBecomeActive() {
    print("Do the refreshing")
}

Another approach is to subscribe to willEnterForegroundNotification notification. For me, didBecomeActiveNotification did not work well because it was also called when system alerts (eg touchID) were dismissed

private func subscribeToNotifications() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(didBecomeActive),
        name: UIApplication.willEnterForegroundNotification,
        object: nil
    )
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM