I want to know how I can get to know when a viewController appears, disappears from the main window from the UIApplication. I don't want to put code in each and every UIViewController, but observe lifecycle of each viewcontroller from the UIApplication.
Every view controller has a life cycle. So every view controller has separate life cycle method. So you have to put code each and every UIViewController. In app delegate we check the application state.
The state's are:
Active state
Inactive state
Background state
Not Running state
Suspended state
Delegate Method:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidBecomeActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationWillTerminate:
View Controller Life cycle method:
-(void)viewDidLoad:(BOOL)animated{
[super viewDidLoad:animated];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
Related
In viewDidAppear I show a popup to users after 3 seconds. What if user navigates to another viewController after timer begins. The selected function will try to execute & show popup when superview is no longer on screen. App does not crash or throw any errors but I want to confirm this is safe. Should I set a BOOL and assert isCurrentView is YES, within selector method?
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self performSelector:#selector(showPopup) withObject:nil afterDelay:2.5];
}
in viewDidDisappear
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:(BOOL)animated];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(showPopup) object:nil];
}
When I call performSegueWithIdentifier before viewDidLoad and viewDidAppear sometimes it works and sometimes not.
AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"applicationDidBecomeActive");
[self abcNotif]; // the method post the notification.
}
VC1:
-(void)awakeFromNib {
NSLog(#"awakeFromNib");
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(theNotif:) name:#"abcNotif" object:nil];
}
-(void)theNotif:(UILocalNotification*)notif {
if([[[NSUserDefaults standardUserDefaults]valueForKey:#"flag"]isEqualToString:#"YES"]) {
[self performSegueWithIdentifier:#"seg1" sender:self];
NSLog(#"theNotif = %#", [[notif userInfo]valueForKey:#"notif1Key"]);
}
}
Logs in console
awakeFromNib
didFinishLaunchingWithOptions
applicationDidBecomeActive
theNotif = notif1Value
viewDidLoad
All is working fine although performSegueWithIdentifier is called before before viewDidLoad and viewDidAppear. But in some scenarios this don't work. Why is this behavior. Peoples have also asked theses kind of questions Why doesn't performSegueWithIdentifier work inside viewDidLoad?
The key thing to understand is that UIViewController subclasses load (create) their view property lazily. So viewDidLoad will be called after the view is loaded, but when is that? Certainly the normal cycle of launching your app causes it at some point, adding it to the window, but you can cause it to happen earlier by making a call on the view. Try inserting [self view]; immediately before your call to performSegue.. , this will ensure the view is loaded. Even so, it does not ensure that the view has been added to the window, and if that hasn't yet happened then I don't see how a modal can be pushed.
I am pausing my game with a willResignActive notification, and it seems to pause the game, but when didBecomeActive is called, it seems to un-pause on its own.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillResign)
name:UIApplicationWillResignActiveNotification
object:NULL];
- (void) applicationWillResign {
self.scene.view.paused = TRUE;
NSLog(#"About to lose focus");
}
How do I get it to stay paused? Do I actually need to pause it in my AppDelegate?
Here's a way to keep the view paused after returning from background mode. It's a bit of a hack, but it does work.
1) Define an SKView subclass with a boolean named stayPaused...
#interface MyView : SKView
#property BOOL stayPaused;
#end
#implementation MyView
// Override the paused setter to conditionally un-pause the view
- (void) setPaused:(BOOL)paused
{
if (!_stayPaused || paused) {
// Call the superclass's paused setter
[super setPaused:paused];
}
_stayPaused = NO;
}
- (void) setStayPaused
{
_stayPaused = YES;
}
#end
2) In the storyboard, change the class of the view to MyView
3) In the view controller, define the view as MyView
4) Add a notifier to set the stayPaused flag
#implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Define view using the subclass
MyView * skView = (MyView *)self.view;
// Add an observer for a method that sets the stay pause flag when notified
[[NSNotificationCenter defaultCenter] addObserver:skView selector:#selector(setStayPaused)
name:#"stayPausedNotification" object:nil];
...
5) In AppDelegate.m, post a notification to set the stay paused flag when the app becomes active
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter] postNotificationName:#"stayPausedNotification" object:nil];
}
I'm getting data off the server via JSON and displaying it on Labels.
I've added that method in viewDidLoad.
I want to refresh the data when the user opens the app again. Currently, even if I kill the app in the simulator and start the app again, it doesn't refresh.
I tried the viewDidAppear method, but it isn't being executed for some reason.
-(void)viewDidAppear{
NSLog(#"Called viewDidAppear");
}
This is never called. I tried to minimize the app but it didn't work.
You can listen for notifications and respond appropriately. Try using these and decide what works for your intended workflow.
UIApplicationDidBecomeActiveNotification
UIApplicationWillEnterForegroundNotification
You can use respond to the notification like this.
[[NSNotificationCenter defaultCenter] addObserverForName: UIApplicationDidBecomeActiveNotification object: nil queue: [NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// LOAD JSON
}];
I followed this tutorial - http://leejon.es/notifying-a-viewcontroller-with-uiapplicationdidbecomeactivenotification/
First, attach to the notification in the viewWillAppear method of the target view controller:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector( appActivated: )
name: UIApplicationDidBecomeActiveNotification
object: nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self ];
}
- (void)appActivated:(NSNotification *)note
{
[self update];
}
The viewDidAppear: method takes a bool parameter wether the view was displayed with an animation which you are missing. Also you have to call the implementation of the superclass:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear: animated];
NSLog(#"Called viewDidAppear");
}
In your app delegate implementation, there is a method called:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
This method is called each time the app is launched, so I think it fits your needs. If you place your code here, it should work.
Also, be aware you should not perform a synchronous call here, because you will delay the app launch.
EDIT:
This method will be only called when the app launches. You could place your code inside a method, and call it from application didFinishLaunchingWithOptions, and then also call it from the method:
- (void)applicationWillEnterForeground:(UIApplication *)application;
This method will be called when the application enters the foreground, but not after the first launch, so beware.
I also think you should check the UIApplicationDelegate methods from apple developer page: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html
Also, check out the application state changes:
http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
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.