How a NSWindow knows it is visible? - cocoa

Is there a ViewDidAppear equivalent method for a NSWindow on cocoa?
What method runs the moment a window is visible?
I know awakeFromNib is triggered but the window is not visible when it fires.

You probably want windowWillLoad and windowDidLoad to perform tasks before the window nib file is loaded or to perform tasks after the window nib file is loaded.
See NSWindowController Class Reference
If you subclass NSWindowController, you can intercept showWindow: messages.

Related

How to Create a Document-Based OS X App that Launches with "Open..." Dialog

I am used to developing non-document-based applications with a single window, but now I am working on a document based application that I created using the document-based template in Xcode 5. When I run my application, it opens a new untitled document upon launch. Instead of automatically creating a new document, I would like my application to display an "Open..." dialog much like Xcode, TextEdit, and other Apple apps do. How do I go about implementing this? Is there a flag somewhere that I can set to show the dialog instead of a new document, or do I have to create an application delegate that shows the dialog upon launch? Thanks for your advice.
That would be customized behaviour.
In your application controller override applicationShouldOpenUntitledFile: to prevent opening a blank document at startup, then display the file dialog.
This is not hard but not obvious and takes a few steps to get going.
Add a window to your MainMenu.xib
Set the Visible at launch to NO in the inspector.
Now create an NSObject subclass in your project. You might include AppDelegate in the name because you will want to make it the delegate of your app.
In the interface header be sure to declare the protocol right after the word NSObject.
While there, add an IBOutlet property an NSWindow.
Back to the MainMenu.xib ...
Add an NSObject (blue cube) to your xib from the library and set its class to your new app delegate class.
Next connect your window to the property in your app delegate class and connect the window's delegate outlet to your app delegate.
Now the menu.
Locate the View menu in MainMenu and add one NSMenuItem.
Give it a title. "My fancy main window" or whatever.
Now connect it to your app delegate with both an IBOutlet (in case you want to fiddle with its enabled state or title later )
And add an IBAction for this menu item as well. Something like showMyFancyWindow:
This menu item will be persistent.
In your IBAction method call makeKeyAndOrderFront: with your app delegate's property for your window as the argument.
Extra credit
Add a BOOL property to your app delegate.
Something like showsMyFancyWindowAtLaunch
Create a constant NSString as a key above your #implementation line.
Add a checkbox button to your window.
Bind its value to your BOOL.
Add an IBAction method for the checkbox.
Inside that
[[NSUserDefaults sharedDefaults] setBool: self.showsMyFancyWindowAtLaunch forKey: theConstStringKeyYouCreated]
Then in your applicationDidFinishLaunching:
Use the corresponding bool:forKey: method of NSUserDefaults to check whether or not to call showMyFancyWindow: method at launch.
My solution, for MacOS 15 and Xcode 13, after fleshing out some of the hints above:
In your AppDelegate (or equivalent) class, turn off the creation of a New "Untitled" document using:
func applicationShouldOpenUntitledFile(_ sender: NSApplication) -> Bool
{
return false
}
Then, in add the following code to your applicationDidFinishLaunching() delegate method:
NSDocumentController.shared.openDocument(self)
Seems to work, though it's impossible to say whether it's a best solution or not.

- (NSWindow *)window doesn't work to show a window while loadWindow does

I am trying to open a nib file called EasyWindow.xib when a button it pressed in my MainMenu.xib. I have the button connected to this IBAction method, but when I click on the button it doesn't open. When I change the "window" in this init part to "loadWindow" it works perfectly fine, but the Mac Developer Library say "You should never directly invoke this method." How do I make the window method work?
- (IBAction)loadEasyWindow:(id)sender
{
[[[NSWindowController alloc] initWithWindowNibName:#"EasyWindow"] window];
}
- (NSWindow *)window only gives you a reference to the actual window object that the NSWindowController manages. It doesn't actually do anything with that window. It's more of a getter than anything else.
Meanwhile, -loadWindow is a method that's called when your program actually loads the window from the nib file and has little to do with opening it, closing it, and showing it. That's why you shouldn't call it. I'm guessing it works because a side effect is the window showing itself.
What you are looking for is probably NSWindowController's - (IBAction)showWindow:(id)sender. [reference] It should do exactly what you want:
Displays the window associated with the receiver

Starting a Cocoa document-based application shows selection window first

This seems like it should be easy yet I must be missing something. I have a document-based application. I have also built a new XIB that has a NSTableView and three buttons on it that I intend to display a list of previous files. I wish this XIB to be displayed instead of the document window when the application first starts. Once the user selects an old file or hits the "New" button I wish to then go to the document window. This is very common and I've seen used quite often.
In my attempts to get this working I have modified the project-info.plist file and changed the Main NIB File Base Name from MyDocument to my Selection XIB name. This causes the application to display the Selection window instead of the MyDocument window. There seems to be no problem up to this point.
In my Selection window I have set up my table view and an array controller and a custom window controller just for this XIB. I have set the File's Owner to the new window controller and bound the window controller's window property to the window and the Window's delegate property to the File's Owner as well as the "Select", "Cancel", and "New" buttons. Nothing is bound to NSApplication. But the strange thing is when I run this application it seems to want to connect these controllers to NSApplication with the error (same for the other two buttons):
Could not connect the action selectButton: to target of class NSApplication
It also displays an error that NSApplication is not Key-Value compliant for the outlet that holds reference to my array. The Array Controller, Window, and buttons are not bound to NSApplication but to the new Window Controller. I would have expected that if there was any problem is would NOT mention NSApplication but rather the window controller to which the controllers are bound.
Anyone know what is happening here? Is this a Target-Action problem because I changed the "Main NIB File Base Name" from "Main Menu" to "Selection"? If I am not supposed to change this, then how can I get Cocoa allow me to display a selection screen before showing the document window?
Any help is greatly appreciated.
Rob
The setting in IB for the class of the File's Owner of the nib is only advisory; it lets IB show only the outlets and actions that are provided by instances of that class. It does not enforce that the File's Owner will be an instance of that class, because the File's Owner is not part of the nib.
The File's Owner is the object that loads the nib. This necessarily means that it is outside of the nib, and nothing in the nib determines anything about it. In the case of the MainMenu nib, its File's Owner—the object that loads the MainMenu nib—is the NSApplication instance. So, everything you hooked up to the File's Owner in your MainMenu nib, you hooked up to the application object, even though you told IB that it wouldn't be the application.
That the application is the owner of the MainMenu nib—regardless of what you tell IB—is not the bug. The application is always the owner of the MainMenu nib. That is normal and correct; you cannot change it, should not attempt to change it, and don't need to change it.
The bug, in a nutshell, is that you are using one nib for two very different purposes.
You should let the MainMenu nib be that alone—containing only the MainMenu, your custom document controller (I'll get to that in a moment), and your app delegate—and move the previous-documents window into a separate nib, owned by the previous-documents window controller. In order to have a window controller be the owner of this nib, you need to have the window controller load it. You must do that in code—you cannot set that up in IB or in a plist.
In your application's delegate, instantiate and own the window controller. It sounds like you made a custom NSWindowController subclass, so you can override its init to have it send itself the initWithWindowNibName: message to load and own the nib. Then, just use alloc and init to create the window controller from the app delegate.
That will get rid of the console message, and ensure that the buttons are actually hooked up to the window controller (because they're hooked up to the File's Owner, which, with this change, will be the window controller).
Have your app delegate respond to applicationOpenUntitledFile: by sending the window controller the showWindow: message. This will make the previous-documents window appear any time the user ordinarily would have created a new document.
If you want to support the usual methods of creating documents (i.e., allow New Document to work), then implement applicationDidFinishLaunching: and applicationShouldHandleReopen:hasVisibleWindows:, not applicationOpenUntitledFile:. Make sure no documents are open, and show your window if that's the case.
You should also make a custom subclass of NSDocumentController and make your document controller an instance of that, and in that class, implement addDocument: and removeDocument: to re-show the previous-documents window when the last open document is closed, and hide it when a document is opened.

Cocoa send background window to front

At the bottom of this post I included an example project that has my code in it. I have a Cocoa Application that uses a main default window, and another window nib I created. When the program starts, I want it to load the window nib and show it in front of the default 'MainMenu' nib window. How can I do this? I've tried the following code, but the window is still displayed behind the default window:
InfoWindowController *winInfo = [InfoWindowController new];
[winInfo.window makeKeyAndOrderFront:self];
[winInfo.window setOrderedIndex:0];
[winInfo.window makeKeyAndOrderFront:self];
[winInfo showWindow:self];
This is being called from AppController, which is a class I added to the MainMenu nib. InfoWindowController subclasses NSWindowController. I have included an example project here.
So, I know i'm reviving an old thread, but I was having a similar issue. Try putting [NSApp activateIgnoringOtherApps:YES]; in there.

Programmatically Removing a Button from a Window in Cocoa OSX

I need to programmatically remove a button from a window in Cocoa but the only method close to this was dealloc in NSObject, but this is not what I want to do. Is there a way to actually delete the button from the window not just deallocate its memory?
Send the removeFromSuperview message to the button instance.
Though perhaps you just want to hide it instead (using setHidden:)?
An NSButton is a subclass of NSControl, which itself is a subclass of NSView.
You should be able to remove the button from it's superview by calling -removeFromSuperView on the button instance.

Resources