Overriding close action in OSX - macos

How do I override the close button action to show a confirm dialog?
Would placing code in the app delegate's
applicationWillTerminate
be the correct place for such code?
I have an app using CloudKit.
It saves way too constantly, where it causes errors trying to save to iCloud.
So I'm trying to show a dialog to confirm if the user wants to sync before quitting.

If you want to block a window from closing, the NSWindow's delegate is where it's at. Be it either the AppDelegate (which is the default for most Xcode templates) or an NSWindowController if that's how you presented your window. The method you want to implement from NSWindowDelegate is -windowShouldClose: or -windowWillClose:. If you want to prevent the closing of the window, simply return NO from -windowShouldClose:.
If you want to stop the app from quitting, you should look into NSApplicationDelegate and override -applicationShouldTerminate: and return NSTerminateCancel to prevent the app from terminating.
In short, the "should" methods allow you to prevent the action from occurring, the "will" methods are just saying that it will happen and you can deal with it.

Related

How do you create a NSWindowController for a xib-based (not storyboard-based) app?

I have an application that's not based on storyboards, but rather xib files. Main.xib contains the main application window. However, it's just a window. There's no NSWindowController. How can one be added?
Ok, figured it out. You simply add a new object to the scene, change its class to whichever NSWindowController subclass you want it to work with, attach the window to its output, then set an output to hold the window controller itself. I recommend on the app delegate.
I took things a step further by also changing the window to not show on launch, and I removed the window from the App Delegate (since it now has a reference to the window controller and thus the window indirectly already). This way I can center the window before actually showing it.
Only caveat to look out for is that you won't get the window-loading overrides since the window is handed to the VC, not loaded by it, so any code you need to run only when the window is set, simply override the window variable and add a didSet section. Works like a charm!
Still, I may try to dig deeper to see if I can update the Window controller to load the window normally so I can get those events as designed.

How can I make a Mouse Event persist through closing an NSPopover?

I have a NSPopover that opens, and if the user clicks somewhere else in the app, the popover closes.
But the problem is that currently that mouseDown event is consumed during the popover-closing process.
Is it possible to still have that mouseDown event go through to the application, but also close the popover?
I had this same problem, so we changed to using NSPopoverBehaviorSemitransient for the behavior type. It no longer steals the mouseDown: and we just added some extra cases for closing the popover manually.
You can subclass the windows contentViewControllers view object.
I did this in the Storyboard file.
In there, implement the mouseDown() method. In there, you can create a notification which can be received at a point in your project where you need to know about the mouse event.
As the 'root view' captures almost all mouseDown() events, you have to filter them in order to only respond to the notification when the popover is displayed.
Don't forget to call super.mouseDown() at the end of your implementation.

Cocoa: Receiving dock icon click of already-running application

I'm writing an application with a main window that's displayed when the app starts. When the window is closed, I'd like the app to remain running (with a menu-bar menu), and if the user clicks on the dock icon again, I'd like the main window to be presented again.
I'm about 90% of the way there: my app properly keeps running after the main window is closed with Cmd-W, and since "Release When Closed" is unchecked, the window could be [makeKeyAndOrderFront:]-ed to show it again when the dock icon is clicked.
The only missing piece of this puzzle is intercepting the actual dock-icon click.
The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller. I've done both, and neither one ever gets called.
Any other ideas?
The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller.
That's only true if the window controller is the application's delegate. That is the object to which the application sends those messages.
I would not make a window controller the application's delegate, though. I typically make them two separate objects. Make one object specifically to be the application's delegate, and when that object receives the relevant delegate messages, send a message to your window controller telling it to do whatever it needs to do.
Actually, what I usually do in single-window apps is make the application's delegate create and own the window controller. You can respond to window closure by throwing away the WC, and respond to reopen by checking whether you have a WC and creating one (and thereby reopening the window) if you don't.
Use [NSApp setDelegate:self]; in awakeFromNib.

How do I create a custom modal NSWindow?

I want to create a custom NSWindow that acts as a modal dialog. By custom I mean it has normal user controls in the window, with a "OK" and "Cancel" buttons. The dialog will contain read only information, and have a few checkboxes, secure edit fields, etc.
The MainMenu.xib file will have the normal Window visible at launch, plus include the custom NSWindow (which is NOT visible at launch).
I am trying to find example code to launch the window in modal mode (after the app initializes and launches main window), and on "OK" run a process, and on success of that process hide the dialog. Or on failure, keep the dialog up, but show an error sheet on the dialog.
Any help is appreciated, thanks.
You want to look at NSApplication’s -runModalForWindow: and/or -runModalSession: methods. Note that using modal windows is generally a bad idea and if it’s at all possible to avoid doing so, you should; that said, sometimes needs must.
As far as launching a process, waiting for it to finish and so on, you can probably do what you need with NSTask, although you don’t provide sufficient detail to be certain. You’d probably want to observe NSTaskDidTerminateNotification to tell you when the task had finished.
See
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/OperatingSystem/OperatingSystem.html
for more on NSTask and
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/WinPanel/Concepts/UsingModalWindows.html%23//apple_ref/doc/uid/20000223-CJBEADBA
for more about modal NSWindow usage.
Have a look at NSApplication's -runModalForWindow: method, and "Using Application-Modal Dialogs."

Determine when a NSOpenPanel will close

I'm trying to determine when an NSOpenPanel is closing before it actually closes. I need to do this so I can overlay another window with a screenshot of the open panel on top of it to be animated. Unfortunately, all the notifications that you seem to be able to access seem to fire AFTER the window's already been closed. This leads to a jarring stutter before you start your transition.
I've tried:
- using NSWindow delegate methods on the open panel (apparently, none of the NSWindow delegate methods work)
- monitoring panel:userEnteredFilename:confirmed: (not called)
- showing the dialog with a callback (callback happens AFTER the panel disappears)
You should register your controller as the open panel's delegate and then implement the -panel:isValidFilename: delegate method. This method will be called just before the open dialog closes.
You should return YES from the method if you just want the notification. Returning NO allows you to prevent the open dialog from being closed.
Another way to handle this was to look through NSOpenPanel's subviews for the Cancel button and swap yourself in as the target/action. This is what i ended up doing.

Resources