How can I make my (non-document-based) app respond to openFile:withApplication:? - cocoa

I have an app, which is a single-window, non-document-based app.
I want to make it respond to NSWorkspace-openFile:withApplication:, but only when the path is to a folder, and also implement the File->Open menu. I'm having trouble tracking down how to do this (without becoming a document-based application).

You have to configure your NSOpenPanel to accept directories:
[myOpenPanel setCanChooseDirectories:YES];

Just check what action the Open menu item is connected to in Interface Builder. If I remember correctly, it would be connected to the "First Responder" object and the method open:. Is that right?
In this case, just implement the open: method in your AppDelegate class. (To understand why the method goes to the delegate, read about "nil-targeted actions" in Hillegass' book, or here: http://www.cocoadev.com/index.pl?NilTargetedAction. The thing to remember is that a control connected to "First Responder" in IB is actually IB's way of denoting that the target is nil.)
Note that you will have to implement the open panel yourself using NSOpenPanel -- see some code for example here: NSOpenPanel setAllowedFileTypes
If this is the same thing as that you're doing in openFile:withApplication:, you will probably want to create a common private method and call that method from both openFile:withApplication: and open:.

Related

XCode 4.6 - Document.xib + MainMenu.xib action from MainMenu shall set outlet in Document.xib

I have a MainMenu which should call an action in an AppController which then should send a message to an outlet of the MainDocument.
When I instantiate an object in Interface Builder in MainMenu.xib this cannot send a message to an outlet in a different xib, as far I understand.
Is there a solution to this?
Most of the default items in the menu are hooked up to that strange "First Responder" placeholder that you see in Interface Builder. Any action message you send to it will get sent through the responder chain which is probably what you want. Read that linked document for more information.
(It's rare that you'd need to hook up an outlet across multiple .xib files.)
If I understand you correctly, you are looking to notify the MainDocument object when you click on a menu item in the MainMenu? If that is the case, one way is to use NSNotification to post the message. You can review the Apple Docs on how to do this here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html#//apple_ref/doc/uid/10000043i

Xcode 4.2 Template Changes - UIApplication & MainWindow.xib

Background: Up until Xcode 4.2, new projects created using any of the templates would contain a MainWindow.xib and therefore pass nil as the fourth argument of UIApplicationMain(). Starting in Xcode 4.2 all the templates instantiate the application delegate by passing the class string as the fourth argument and do not build the application's window in a xib.
It is trivial to accomplish this setup in 4.2, and of course it works as expected: create xib setting File's Owner to UIApplication and wire up the delegate, specify it in Info.plist, nil fourth argument in main().
Question: Why is Apple encouraging instantiating the application delegate and building the UIWindow in code now instead of the "old way?" What are the benefits?
Considerations: I would expect this new template behavior if you elect to use storyboarding as a way to manage the UI, but if you uncheck "Use Storyboards" I would have expected the old pass-nil-and-use-MainWindow.xib template.
This question was asked in a roundabout way here, but the answers are a little thin on discussion.
You're asking why Apple is doing something? There can be no definitive answer, unless Apple has spoken out explicitly, which they have not done.
Personally I find the new approach considerably more elegant, transparent, and bulletproof. As you rightly say, in the old approach the main nib was loaded automatically by the runtime in response to the Info.plist setting, and everything else that happened was done through the nib, in particular the instantiation of the app delegate and the window and the associated wiring (the app delegate must be made the application delegate, the window must be made the app delegate's window), except that then we come back to the code in the app delegate for final presentation of the interface.
This was hard to understand; it took a great deal of verbiage for me to describe it in my book. It was also easy to break. The nib has to know the name of the app delegate class, so if you didn't like those funny long names that were created by default, you could easily mess everything up when you changed them.
Now, however, the app delegate is simply named App Delegate and is instantiated in code by UIApplicationMain(), as you rightly say; and everything else is also done in code as a direct follow-on: the app delegate is instantiated and didFinishLaunching is called, whereupon we create the window in code, assign it to our property in code, load the nib if there is one in code, set the window's rootViewController in code, and show the interface in code as before.
Thus the bootstrapping is directly exposed to view because it's all code. This makes it easier to understand and to modify without breaking anything. It's almost as if previously the template designer was just showing off as to how much stuff could be made to happen magically and automatically behind the scenes; now everything happens out in the open, explicitly.

file's owner of xib is not what i expect

i'm using xcode4.
i have a xib window (perhaps copyed by another one, i forgot because i have made it some times ago) named: chooseCharacter.xib
i have the chooseCharacter.h and .m as view controller (i thought)
i have added
-(IBAction)doneButtonClick;
on the chooseCharacter.h but i've not seen it in interface builder actions...
i have added this on HighScoreViewController.h and it now shows up...
however, if i put it on HighScoreViewController.h i must put the implementation in chooseCharacter.m or i get
-[ChooseCharacter doneButtonClick]: unrecognized selector sent to instance 0xcc0d9b0'
seems that the window is binded with a file's owner that is HighScoreViewController.h but search implementation in ChooseCharacter.m!!!
how can i bind the xib to the right file?
thanks
found it.
selecting in interface builder, file's owner and clicking in identity inspector under class was HighScoreViewController, changed to chooseCharacter now it works

Xcode & IB - Window Controller Method

I have 2 windows in an xcode project, A and B. A is to capture information, B is to display. I built the windows in IB.
I would like to create a method to control the submit from window A to close window A, and display window B fullscreen.
I am completely new to OBJ C and Cocoa, so Please explain this or provide example code...
If I want to do this, I know I need to create a file from within IB with my A and B to add to my project to add the code, or do I simply add a cocoa file .h and .m to the project. If so, what tells IB that these files correspond to the windows I already created in IB. Once the IBAction is completed I know how to link in IB, but I am at a loss as to how to proceed.
So from what it sounds, you need to declare (in .h):
- (IBAction) closeWindowA:(id)self;
Then tell your application what closeWindowA really does (in. m):
- (IBAction) closeWindowA {
// your code goes here. Look up method(s) for closing the window - don't know them by heart
}
Then, just connect your Button or whatever is triggering the action in Interface builder using the draggable connections. Hope this helps - I also have to recommend 'Cocoa Programming for Mac OSX' by Aaron Hillegas. Helps tremendously in understanding these kinds of things.

How do I dismiss an NSPanel when creating or opening a new document?

I am working on a document-based Cocoa application. At startup, the user is presented with a "welcome panel" (of type NSPanel) with buttons for common actions like "Create New Document" and "Open Existing Document". These actions are linked to the first responder's newDocument: and openDocument: actions, respectively, just like the matching items in the File menu.
Everything works as expected...with three caveats:
The welcome panel is not dismissed when creating or opening a new document.
Document windows do not have focus when they are created.
Open document windows do not have the open file represented in the window title bar; likewise, new document windows do not get created with titles like "Untitled", "Untitled 2", "Untitled 3", etc., as expected. (I'm mentioning this not only because it's annoying, but because it may yield some insight into what's going wrong.)
I have partially solved #1 by making my application controller a delegate of the welcome panel. When clicking the "Open Existing Document" button, the panel resigns its key status (since a file browser dialog is being opened), so I can close the panel in the delegate's windowDidResignKey: method. However, I can't figure out how to close the panel when creating a new document, since I can't find a notification that is posted, or a delegate method that is called, when creating a new document. And ultimately, #2 is still a problem, since the document windows don't gain focus when they're created.
I have only subclassed NSDocument -- I'm not using a custom document or window controller at all. I've also tried changing the panel to an NSWindow, thinking that an NSWindow may behave differently, but the same problems are occurring.
Make a custom document controller, and have it know about your Starting Points panel's controller, and hide the window in addDocument: and show it again (if no other documents remain) in removeDocument:.
This is what we did in Adium Xtras Creator. That code is under a BSD license (unlike Adium proper), so you can borrow it if you want.
Instead of linking to the first responder's default actions, just create custom action method in your window controller and set your buttons to trigger those actions. In your method, you need to close the welcome window and then create a new document.
Something like this:
- (IBAction)createNewDocument:(id)sender
{
//this will close the window if you're using NSWindowController
[self close];
[[NSDocumentController sharedDocumentController] newDocument:sender];
}
Or if you're not using an NSWindowController for your welcome window you can just message the window directly:
- (IBAction)createNewDocument:(id)sender
{
//assume you have a "window" outlet connected to your welcome window
[window orderOut:sender];
[[NSDocumentController sharedDocumentController] newDocument:sender];
}

Resources