Closing child windows in Cocoa when the main window is closed - objective-c

I'm a Cocoa newbie so it is likely that my approach is wrong but ..
I have an app which opens several child windows (after the main/parent window has been loaded) using NSWindowController and initNibWIthName:. This works fine.
But when I close the parent window (using the red x) these remain open and prevent the app from closing until they are closed as well. This makes sense as I am not shutting them anywhere.
But how do I do this? There must be an event that is called at this point but I can't find what it is anywhere.
Notifications such as applicationWillTerminate (and so on) are only called when the application actually is terminating not when the close button has been pressed.
I guess I'm looking for something similar to the Windows WM_CLOSE type messages.

The closest equivalent you'll find is the NSWindowWillCloseNotification posted by the window prior to its closing. You can probably get the child windows to close themselves when the parent window closes using:
NSWindow *parentWindow;
NSArray *childWindows;
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
for (NSWindow *childWindow in childWindows) {
[noteCenter
addObserver:childWindow selector:#selector(close)
name:NSWindowWillCloseNotification object:parentWindow];
}
If the child window will be deallocated before its parent, be sure to unregister it for notifications before that happens.
The delegate method mentioned by Mark is a convenience method for the delegate that saves them the trouble of registering for a notification they'll likely want anyway. You don't need to create a window controller just to receive that message; simply sending the window [window setDelegate:myObject] will cause myObject to receive the -windowWillClose: message if it responds to the method.
By the way, what Cocoa calls "child windows" differs from what you're thinking of. They're not addressed in the Window Programming Guide, but if you look at the documentation for the related methods on NSWindow, you'll see that they basically track the movements of their parent window, so that they move with it.
If you're coming to Cocoa from Win32 programming, you might find Apple's Porting to Mac OS X from Windows Win32 API helpful to highlight conceptual differences between Win32 and Cocoa.

windowWillClose:
Apple developer docs NSWindowDelegate

Windows and applications are not the same thing in Mac OS X.
If you have a single-window interface, with a main window and no others except for About, Preferences, etc., then you should implement applicationShouldTerminateAfterLastWindowClosed: in your application delegate and return YES. This is the only way (aside from you doing it manually) that closing a window causes the application to quit.
If you have a multiple-window interface (as in a typical document-based application), then you should make all of those windows peers to one another. Windows such as Inspectors and tool palettes should be floating panels, not regular windows. And closing the last window should never, ever quit such an app.

Related

Mac OS X -- receive notification when frontmost window changes

I am wondering whether there is a way in Mac OS X to receive a notification when the frontmost window switches to a different window -- either an Objective-C solution, or Python, or AppleScript, or something else. I want to look at the whole system, not just within my application. My app is trying to keep track of what file the user is currently working on, and I have a polling solution that gets the frontmost app and frontmost window every so often by running an AppleScript, but it would simplify my life if I could run that check only when I knew that the frontmost window had changed.
I've also looked at NSDistributedNotificationCenter and global event monitors for NSEvents, which are both useful in different ways, but don't seem able to give me the specific front-window-change notification that I'm ideally looking for.
Any ideas on directions I should try, or whether this is even possible, would be greatly appreciated!
I don't know of a way to get a notification when a window changes, however in objective-c you can get a notification when things happen at the application level. That might help you.
You want to register for NSWorkspace notifications...
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:#selector(nsworkspaceNotification:) name:nil object:nil];
Look at the bottom of the NSWorkspace class documentation for the notifications. Some that would help you are: NSWorkspaceDidLaunchApplicationNotification, NSWorkspaceDidActivateApplicationNotification, NSWorkspaceDidDeactivateApplicationNotification, NSWorkspaceDidHideApplicationNotification, NSWorkspaceDidUnhideApplicationNotification. There may be others.
Good luck.
I think you would capture NSWindowDidBecomeMainNotification. The notification object contains NSWindow.
best,

NSWorkspaceDidActivateApplicationNotification called before application is ready to use (launched)

I'm using both NSWorkspaceDidActivateApplicationNotification and NSWorkspaceDidLaunchApplicationNotification notifications to know which app the user is interacting with.
The problem is that, if an application is just opened and still launching, I first receive a activate notification, and soon afterwards a launch notification.
Is there any way to know within the activate method that the app is still launching and not yet ready for use? (Still bouncing in the dock)
I see that the ichat sample project by apple does not use the above approach and instead only listens to launch notifications. It then uses kAXApplicationActivatedNotification to add an AXObserver to the app. Is this the preferred way? (And also NSRunningApplications to add an observer to all already loaded apps).
I wanted to keep using just plain simple NSNotifications because I think it may be less memory intensive. (No need to keep an observer around for each and every app loaded).
check the NSRunningApplication object passed in the userinfo of the NSWorkspaceDidActivateApplicationNotification
NSRunningApplication *app = [note.userInfo objectForKey:NSWorkspaceApplicationKey];
if(app.isFinishedLaunching)
NSLog(#"up");

applicationWillTerminate not getting called on force quit of iOS app

Does anyone have any insights into when/under what conditions applicationWillTerminate is called in iOS 5/6?
I've got some logic i'd like to execute whenever the application terminates (not moves to the background), for example if the user navigates to the application bar at the bottom of the screen by double tapping the home button and force quits the app.
when i try to do this on a test device, applicationWillTerminate does not seem to get called. Is there a reason for this?
My plan B is to tie that logic to some persistent object like a singleton or a static that is automatically destroyed when the app quits.
Any suggestions?
thanks
Have you read the documentation for applicationWillTerminate:,
It says,
For applications that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the application. For applications that support background execution, this method is generally not called when the user quits the application because the application simply moves to the background in that case. However, this method may be called in situations where the application is running in the background (not suspended) and the system needs to terminate it for some reason.
There is a "maybe" mentioned there. Probably that answers your question. So it is not necessary that this will get called when you quit the app. Probably you might have to use UIApplicationExitsOnSuspend to disable multitasking and then it might get called while putting in background. But that again depends on your app requirement. If you cannot disable multitasking, you might have consider doing that in applicationDidEnterBackground method or so. I am not sure if there are any other delegate methods which will help in identifying the force quit.

Cocoa window dragging

I'm using addGlobalMonitorForEventsMatchingMask to listen to events in Cocoa:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDraggedMask
handler:^(NSEvent *event) {
NSLog(#"Dragged...");
}];
Though I would like to know if I'm dragging/moving a window (and which window that is, I can find the focused window though when holding command and dragging a window it doesn't get focus as far as I know.)
So, can I detect whether or not I'm dragging a window?
Update:
I now have a class: "SATest : NSObject <NSWindowDelegate>" in which I implement the windowDidMove method (and later perhaps windowWillMove too.) Though, now the next step would be attaching this to a window, right? So my question now is: How do I attach the delegate to all windows of all apps?
Update 2:
I now can find a list of all open windows on the screen:
AXUIElementRef _systemWideElement;
_systemWideElement = AXUIElementCreateSystemWide();
CFArrayRef _windows;
AXUIElementCopyAttributeValues(_systemWideElement, kAXWindowsAttribute, 0, 100, &_windows);
Now I have to iterate over the windows, and of each get it's NSWindow so I can add my delegate to it: [window setDelegate:self];
Update 3: To be clear, this question is about detecting the dragging of ALL windows of ALL apps. Not only the windows of my own app.
Also, I'm very new to this event and window management stuff, so no need to keep your answer short I'm happy to read a lot :P
Thanks!
-P
To find out if a window is being dragged you need to have an object that acts as the delegate of the window by responding to the following messages of the NSWindowDelegate protocol:
windowWillMove - this tells the delegate that the window is about to move.
windowDidMove - this tells the delegate that the window has moved.
You can retrieve the NSWindow object in question by sending object to the notification parameter sent to these methods:
e.g.
NSWindow draggedWindow = [notification object];
More information can be found here.
Update:
In response to your request about getting this information for all windows the NSApplication class provides a method which returns an array of all windows owned by the application. The typical way of getting this information is to use one of the NSApplicationDelegate methods to get a reference to your application object.
For example, in your app delegate (pseudocode):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSApplication * app = [aNotification object];
// you now have a reference to your application.
// and can iterate over the collection of windows and call
// [window setDelegate:self]; for each window.
}
Note that you will need to add / remove you delegates as windows are added and removed. The best method for doing this is – applicationDidUpdate:.
This should be enough to get you started solving your problem.
As suggested by Benjamin the answer lies in the accessibility API. I was looking around in this for a while, even before I asked this question, but never got it to do what I wanted. I now have found a pretty nice solution.
At a high-level I do the following:
Listen to the mouse down event and remember both on which window you clicked and it's location.
Listen to the mouse up event and check if the location has changed, if so you know you moved a window
You can do something similar for the size if you also want to know if you resized. There might be a better solution, but after days of trying stuff this is the only way I got it to work the way I wanted.
Hope this helps anyone who was looking for something similar.
-Pablo

What message is sent to a UIView when the application terminates?

I am trying to locate a message which I can override and then save changes to my application.
On MainWindow.xib I have placed a UIView and set its class (in interface builder) to be my Custom view TouchDrawView.
In TouchDrawView I have a bunch of code for handling touch events and 2 arrays which track these touch events.
My application is launched by the AppDelegate but it has no reference to this TouchDrawView. It simply launches the application.
What I want to do is save my 2 arrays when the application terminates - I can do this in the TouchDrawView but I don't know what message this UIView gets sent when the whole application is about to terminate and I can't do it in the AppDelegate because it doesn't have a reference to the 2 array or the custom UIView
UIView instances will not get send any messages when the app will terminate.
There's another easy way to get notified of app state changes: notifications. You can register for notifications sent through [NSNotificationCenter defaultCenter].
For older versions of iPhone OS there's a UIApplicationWillTerminateNotification. Beginning from iOS 4 you should also listen for UIApplicationDidEnterBackgroundNotification, to prepare for termination.
The short, unhelpful answer is that a UIView is not sent a message when the application terminates.
You need to think in terms of Model-View-Controller because that's how Cocoa and Cocoa Touch are designed. Of those three, if a message was directly sent to any it would be the controller. In your case that would be the UIViewController that talks to your view.
The bad news is there is no such message there either.
An application shuts down, not an individual screen/view.
There are (at least) two ways of doing what you want to do. Firstly, you could save the state of the view controller when it is released. (That kind of feels wrong to me.)
They way that I do it in my app is to listen for the applicationWillTerminate message in your UIApplicationDelegate and traverse the view hierarchy and save the state of each view controller. When the system starts up you can do the opposite. I blogged about it here.