Code on window closing in App Delegate? - objective-c

I have a Mac application where I would like to save some values when the window closes. However, I can't figure out how to do this. I have the application delegate controlling the main window (which may not be the way you are supposed to do it, and I probably shouldn't have done it this way if what I've read previously is correct) and I can't figure out for the life of me how to do this! I believe it can be done using a NSWindowController, but can you do it in the app delegate? Thanks!

Use NSWindowWillCloseNotification.
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
}
- (void)windowWillClose:(NSNotification *)notification {
// Your code for saving data
}

Related

Load Title ViewController When applicationDidBecomeActive:

I've created an app that has two viewcontrollers. The app opens to a title screen (general UIViewController titled 'Title') with a segue connection to the second view that is a custom class (OSViewController titled 'MapView'). As it is, the app suspends when entered into the background state so it opens right where you left off which is typically in MapView.
I want to know what I need to do to have the app start at the title screen when it becomes active. Preferably, I'd like it to open to the title screen if it is inactive for more than 1 minute. From what I've been reading, it seems like I would make a call in applicationDidBecomeActive: method in my AppDelegate to code this in. Please provide me the code to put in the applicationDidBecomeActive: method (if that's the right place to put it) that will reopen my app to the title screen when transitioning from the inactive state to the active state. My app is almost finished but I'd like to fix this issue and I don't have a lot of experience dealing with app states. Thanks in advance for your time.
If you need more information just ask.
You can also register a class as an observer of the "didBecomeActive" notification. You should place this in the viewDidLoad or the init method of your class.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
In this case, willBecomeActive: is a method that you have defined in your class that get's called when the app becomes active again. That might look something like this:
- (void)willBecomeActive:(NSNotification *)notification {
if (self.navigationController.topViewController == self) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
You'll also need to add this in your viewDidUnload method
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
EDIT:
Thanks #AMayes for the advice. I don't believe key/value observing is necessary in this instance.

Execute code when app reopens

I have a Single View Application. When I hit the home button and ‘minimise’ the application I want to be able to execute code when the user reopens it.
For some reason viewDidAppear and viewWillAppear do not execute when I minimise and reopen the application.
Any suggestions?
Thanks in advance
sAdam
You can either execute code in the app delegate in
- (void)applicationDidBecomeActive:(UIApplication *)application
or register to observe the UIApplicationDidBecomeActiveNotification notification and execute your code in response.
There is also the notification UIApplicationWillEnterForegroundNotification and the method - (void)applicationWillEnterForeground:(UIApplication *)application in the app delegate.
To hook up notifications add this at an appropriate point
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Define a the corresponding method
- (void)didBecomeActive:(NSNotification *)notification;
{
// Do some stuff
}
Then don't forget to remove yourself an observer at an appropriate point
[[NSNotificationCenter defaultCenter] removeObserver:self];
Discussion
You most likely only want your viewController to respond to events whilst it is the currently active view controller so a good place to register for the notifications would be viewDidLoad and then a good place to remove yourself as an observer would be viewDidUnload
If you are wanting to run the same logic that occurs in your viewDidAppear: method then abstract it into another method and have viewDidAppear: and the method that responds to the notification call this new method.
This is because since Apple implemented "Multitasking", apps are completely reloaded when you start them again, just as if you had never closed them. Because of this, there is no reason for viewDidAppear to be called.
You could either implement
- (void)applicationWillEnterForeground:(UIApplication *)application
and do there what ever you want. Or you register for the notification UIApplicationWillEnterForegroundNotification in your view controller. Do this in viewDidLoad:
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myAppWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification object:nil];
And of course implement the specified selector and do there what you want.
I am not sure how the answer by #Paul.s performs the OP request since registering UIApplicationDidBecomeActiveNotification will be executed twice:
When launching the app
When application goes into the background
A better practice will be to decouple those events into 2 different notifications:
UIApplicationDidBecomeActiveNotification:
Posted when the app becomes active.
An app is active when it is receiving events. An active app can be said to have focus. It gains focus after being launched, loses focus when an overlay window pops up or when the device is locked, and gains focus when the device is unlocked.
Which basically means that all logic related to "when application launched for the first time"
UIApplicationWillEnterForegroundNotification:
Posted shortly before an app leaves the background state on its way to becoming the active app.
Conclusion
This way we can create a design that will perform both algorithms but as a decoupled way:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName1) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName2) name:UIApplicationDidBecomeActiveNotification object:nil];
This because you don't redraw your view. Use applicationWillEnterForeground in the AppDelegate instead. This should work fine for you.

How to simulate applicationWillResignActive message?

How can one simulate applicationWillResignActive to be called?
Locking screen, going to main menu, simulating phone call - none seemed to have helped.
In case I expect things that won't happen, let me tell you more: I subscribe to this message and when that happens hope that notification is sent to a method as listed below:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name: UIApplicationWillResignActiveNotification object:app];
Your code is valid, it works for me. And locking the screen or hitting the home button will cause this notification to be posted.
*(One caveat to this is if your device does not support multitasking or if you have the "Application does not run in background" property set to yes in your *info.plist. In which case it will go straight to the "UIApplicationWillTerminateNotification" notification)
So barring that there there are two possiblities:
1) Your addObserver code is not being called, ie. It's in the wrong method.
To test try this:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
NSLog(#"Observer added");
2) The observer method is not being called properly. Which requires the same method as the at selector.
To test try this:
-(void)applicationWillResignActive:(NSNotification *)notification{
NSLog(#"applicationWillResignActive");
}
Just as an additional point, if you want to see in action which of these notifications are called and when. In your AppDelegate class put the line NSLog(#"%#", NSStringFromSelector(_cmd)); in each of the -application... methods and it will log them when they are called. It's a nice hands on way to get to know them.

Calling a method when the application is called from background

In applications like Foursquare when I click the Home button the application goes to background. Then when I click on its icon, it loads back the content on the screen.
When I send my app to background and then I recall it back, it doesn't load back the content to the screen. I have entered my code in the viewDidAppear method but it is not executed.
How is it possible to load the application content when it becomes active?
You need to respond to - (void)applicationDidBecomeActive:(UIApplication *)application or - (void)applicationWillEnterForeground:(UIApplication *)application or the equivalent UIApplication notifications. The UIViewController lifecycle calls like viewDidAppear aren't triggered by app lifecycle transitions.
smparkes suggestion is right. You could register for UIApplicationDidBecomeActiveNotification or UIApplicationWillEnterForegroundNotification. These notifications are called after those method (the ones smparkes wrote) are called. In the handler for this notification do what you want. For example in viewDidLoad for your controller register the following notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doUpdate:)
name:UIApplicationDidBecomeActiveNotification object:nil];
Do not forget to remove in dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Finally, doUpdate method could be the following
-(void)doUpdate:(NSNotification*)note
{
// do your stuff here...
}
I suggest you to read UIApplicationDelegate class reference. In particular read about Monitoring Application State Changes.
Hope it helps.
Suppose you want to listen to UIApplicationDidBecomeActiveNotification,here is the ObjC code that might help you.
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification
object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// custom code goes here.
}];

Detecting if ANY window is being dragged

Is there a way (without using any undocumented API) to figure out if ANY window (not just the application from which the code is running from) is being dragged?
I can use
[NSEvent addGlobalMonitorForEventsMatchingMask:]
but this is just for general dragging and there's no way to tell if a window is beign dragged or not.
Thanks!
Update: I think the answer might lie with these two functions:
CGSGetWindowBounds
CGSNewRegionWithData
If someone can tell me what these functions do and where I can find the documentation for them, it would be great! Thanks.
Your only viable, system-supported API is the Accessibility Framework. You can get notifications this way for other applications' windows but access to read/modify is limited to position/size.
I'm not sure if there's a better approach, but here's one way to do it:
Create an BOOL ivar that tracks whether a window is being moved or not. Then register for the NSWindowWillMoveNotification and NSWindowDidMoveNotication notifications:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(windowWillMove:) name:NSWindowWillMoveNotification object:nil];
[nc addObserver:self selector:#selector(windowDidMove:) name:NSWindowDidMoveNotification object:nil];
Then handle the notifications and set the ivar appropriately:
- (void)windowWillMove:(NSNotification*)notification
{
windowBeingDragged = YES;
}
- (void)windowDidMove:(NSNotification*)notification
{
windowBeingDragged = NO;
}
Now you can just check the value of the ivar (windowBeingDragged in this case, to check if a window is being dragged).