I am just coding my first iOS app using a today widget (using Swift). I was wondering if there is a function that is called whenever my app comes back to the foreground after dismissing the notification center.
I know I can use an Observer to check for UIApplicationWillEnterForegroundNotification but my function does not get called when pulling down the notification center while using my app and dismissing it again.
My problem is simple:
It is quite unlikely users will pull down the notification center to manipulate data I am using in the app, but I still have to consider what happens if they do. The user is supposed to be able to save his current location by pressing the today widget button.
If that happens while using my app, the app won't check for new data.
I used the following code for determining if the notification center was opened during the application's run time:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
{
BOOL notificationCenterCurrentlyDisplayed;
}
- (void) viewDidLoad
{
[super viewDidLoad];
notificationCenterCurrentlyDisplayed = false;
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:#selector(onNotificationCenterDisplayed) name:UIApplicationWillResignActiveNotification object:nil];
[defaultCenter addObserver:self selector:#selector(onNotificationCenterDismissed) name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void) onNotificationCenterDisplayed
{
notificationCenterCurrentlyDisplayed = true;
NSLog(#"Notification center has been displayed!");
}
- (void) onNotificationCenterDismissed
{
// Reason for this check is because once the app is launched the UIApplucationDidBecomeActiveNotification is called.
if (notificationCenterCurrentlyDisplayed)
{
notificationCenterCurrentlyDisplayed = false;
NSLog(#"Notification center has been dismissed!");
}
}
#end
Also the notification center was displayed method will also be called when the user decides to close the application into the background.
My code for application will Enter Foreground is given below. I am working on the iOS simulator. What I am trying in my code is, when a player clicks home button while game is going on and returns back to the game after sometime, I want the game to be in pause state. Although my code pauses the game, but it does not pause it immediately. That is, when I go to my game again, there is 1 second of movement before everything pauses.
-(void)applicationWillEnterForeground:(UIApplication *)application
{ [[NSNotificationCenter defaultCenter] postNotificationName:#"didEnterForeground" object:self];
}
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(handleEnterFg)
name: #"didEnterForeground"
object: nil];
-(void) handleEnterFg
{
if (gameIsOver== NO)
{
[myTimer pause];
gamePause = YES;
self.scene.view.paused = YES;
}
}
-(void) handleEnterBg
{
if (gameIsOver== NO)
{
[bgPlayer pause];
[self childNodeWithName:#"pauseButton"].zPosition = -10;
[self childNodeWithName:#"playButton"].zPosition = 160;
}
}
Thank you!
You need to pause the game when you receive applicationWillEnterBackground if you want to pause it immediately.
I need to pause the game before my game goes to background, so when it comes to foreground again I see the pause dialog in my game. To show pause dialog I have a scene method. How to call scene method right before application goes to background? I see that the following method is the right place:
-(void) applicationDidEnterBackground:(UIApplication*)application
{
if( [navController_ visibleViewController] == director_ )
[director_ stopAnimation];
}
Should I just get the scene from director runningScene, cast it to my scene class and then send pause message to it or that way is ugly?
In this scenario, I think Notifications are the cleanest and safest option. You can just post a notification from applicationDidEnterBackground and receive wherever you want to perform certain actions like:
In App Delegate:
- (void)applicationDidEnterBackground:(UIApplication*)application {
[[NSNotificationCenter defaultCenter] postNotificationName:#"EnteringBackground" object:nil];
}
In GameScene:
- (void)onEnter {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showPausePopup) name:#"EnteringBackground" object:nil];
}
- (void)onExit {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)showPausePopup:(NSNotification*)notification {
// Code to show popup
}
I have a View Controller in which my value is 0 (label) and when I open that View Controller from another ViewController I have set viewDidAppear to set value 20 on label. It works fine but when I close my app and than again I open my app but the value doesn't change because viewDidLoad, viewDidAppear and viewWillAppear nothing get called. How can I call when I open my app. Do I have to do anything from applicationDidBecomeActive?
Curious about the exact sequence of events, I instrumented an app as follows: (#Zohaib, you can use the NSNotificationCenter code below to answer your question).
// AppDelegate.m
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"app will enter foreground");
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"app did become active");
}
// ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"view did load");
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
- (void)appDidBecomeActive:(NSNotification *)notification {
NSLog(#"did become active notification");
}
- (void)appWillEnterForeground:(NSNotification *)notification {
NSLog(#"will enter foreground notification");
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"view will appear");
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"view did appear");
}
At launch, the output looks like this:
2013-04-07 09:31:06.505 myapp[15459:11303] view did load
2013-04-07 09:31:06.507 myapp[15459:11303] view will appear
2013-04-07 09:31:06.511 myapp[15459:11303] app did become active
2013-04-07 09:31:06.512 myapp[15459:11303] did become active notification
2013-04-07 09:31:06.517 myapp[15459:11303] view did appear
Enter the background then reenter the foreground:
2013-04-07 09:32:05.923 myapp[15459:11303] app will enter foreground
2013-04-07 09:32:05.924 myapp[15459:11303] will enter foreground notification
2013-04-07 09:32:05.925 myapp[15459:11303] app did become active
2013-04-07 09:32:05.926 myapp[15459:11303] did become active notification
Using Objective-C
You should register a UIApplicationWillEnterForegroundNotification in your ViewController's viewDidLoad method and whenever app comes back from background you can do whatever you want to do in the method registered for notification. ViewController's viewWillAppear or viewDidAppear won't be called when app comes back from background to foreground.
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doYourStuff)
name:UIApplicationWillEnterForegroundNotification object:nil];
}
-(void)doYourStuff{
// do whatever you want to do when app comes back from background.
}
Don't forget to unregister the notification you are registered for.
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Note if you register your viewController for UIApplicationDidBecomeActiveNotification then your method would be called every time your app becomes active, It is not recommended to register viewController for this notification .
Using Swift
For adding observer you can use the following code
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: "doYourStuff", name: UIApplication.willEnterForegroundNotification, object: nil)
}
func doYourStuff(){
// your code
}
To remove observer you can use deinit function of swift.
deinit {
NotificationCenter.default.removeObserver(self)
}
Swift 3.0 ++ version
In your viewDidLoad, register at notification center to listen to this opened from background action
NotificationCenter.default.addObserver(self, selector:#selector(doSomething), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
Then add this function and perform needed action
func doSomething(){
//...
}
Finally add this function to clean up the notification observer when your view controller is destroyed.
deinit {
NotificationCenter.default.removeObserver(self)
}
Swift 4.2. version
Register with the NotificationCenter in viewDidLoad to be notified when the app returns from background
NotificationCenter.default.addObserver(self, selector: #selector(doSomething), name: UIApplication.willEnterForegroundNotification, object: nil)
Implement the method that should be called.
#objc private func doSomething() {
// Do whatever you want, for example update your view.
}
You can remove the observer once the ViewController is destroyed. This is only required below iOS9 and macOS 10.11
deinit {
NotificationCenter.default.removeObserver(self)
}
Just have your view controller register for the UIApplicationWillEnterForegroundNotification notification and react accordingly.
I think registering for the UIApplicationWillEnterForegroundNotification is risky as you may end up with more than one controller reacting to that notification. Nothing garanties that these controllers are still visible when the notification is received.
Here is what I do: I force call viewDidAppear on the active controller directly from the App's delegate didBecomeActive method:
Add the code below to - (void)applicationDidBecomeActive:(UIApplication *)application
UIViewController *activeController = window.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
activeController = [(UINavigationController*)window.rootViewController topViewController];
}
[activeController viewDidAppear:NO];
try adding this in AppDelegate applicationWillEnterForeground.
func applicationWillEnterForeground(_ application: UIApplication) {
// makes viewWillAppear run
self.window?.rootViewController?.beginAppearanceTransition(true, animated: false)
self.window?.rootViewController?.endAppearanceTransition()
}
As per Apple's documentation:
(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated;
Description:
Tells a child controller its appearance is about to change.
If you are implementing a custom container controller, use this method to tell the child that its views are about to appear or disappear. Do not invoke viewWillAppear:, viewWillDisappear:, viewDidAppear:, or viewDidDisappear: directly.
(void)endAppearanceTransition;
Description:
Tells a child controller its appearance has changed.
If you are implementing a custom container controller, use this method to tell the child that the view transition is complete.
Sample code:
(void)applicationDidEnterBackground:(UIApplication *)application
{
[self.window.rootViewController beginAppearanceTransition: NO animated: NO]; // I commented this line
[self.window.rootViewController endAppearanceTransition]; // I commented this line
}
Question: How I fixed?
Ans: I found this piece of lines in application. This lines made my app not recieving any ViewWillAppear notification's. When I commented these lines it's working fine.
I want to use UIWebView to load URL and play vedios. When I press done button in MediaPlayer control on the UIWebView, I want to do something.
My Question is, in this case can it be OK, or does the UIWwebView has a delegate method to do after pressing done button?
The "Done" button only shows up in full screen mode. You can detect the end of full screen mode by observing the #"UIMoviePlayerControllerDidExitFullscreenNotification" mode:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerDidExitFullscreen:)
name:#"UIMoviePlayerControllerDidExitFullscreenNotification"
object:nil];
}
- (void)viewDidUnload
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)moviePlayerDidExitFullScreen:(NSNotification *)notification
{
// This is where you do whatever you want because the user pressed "Done".
}
The UIMoviePlayerControllerDidExitFullscreenNotification is not documented, so I don't know if it will pass App Store review. If you're not planning to distribute via the App Store, it shouldn't matter.