Ok I hope this is a helpful answer.
First I'm going to discuss a base app development difference between iOS and Mac, and will continue onto how to work with NSWindowController and then try to explain why what you did caused unexpected behavior!
Differences Between iOS and Mac
Nibs
So something that's interesting in Mac development (I as well came from iOS development) is there isn't the same function call UIApplicationMain
in AppKit; that is there is a function NSApplicationMain
; however their signatures are different. In iOS you can specify the UIApplication
subclass and UIApplicationDelegate
class for the app to use, but in Mac you cannot specify the NSApplication
subclass and the NSApplicationDelegate
class. With that said one might be confused how the AppDelegate
ever works, or how one would use a custom NSApplication
. First in your Info.plist
there is a key NSPrincipalClass
which tells the system what subclass of NSApplication
to use for the app. Unfortunately the delegate isn't as simple. The Info.plist
also has another key NSMainNibFile
which tells the system what nib to initially use. It loads this nib with the application as the Owner and automatically adds some default connections:
- App Delegate: Here is where your app delegate is set. If you notice this object has class
AppDelegate
(orPREFIXAppDelegate
). So here the nib will automatically allocate one of these classes and assign the delegate connection to the file owner, thus setting the delegate on theNSApplication
. - Font Manager: This is going to be representative of the shared
NSFontManager
on Mac. It adds some connections to the menu items which keep in line with the system UX of choosing fonts. - Main Menu: This is the top menu you see. If you ever want to customize this you can do so here in this nib.
So why use nibs? I personally came from iOS as I mentioned, and personally there wasn't TOO fond of nibs all the time. Things simply felt more flexible doing it all in code. Well in a Mac app I don't feel like you lose that flexibility in using nibs. Nibs are generally the standard (as far as I can tell) for creating a mac app (and standards usually exist for a reason). Further, a fairly key aspect of creating a Mac app is setting the NSMainNibFile
key which loads a nib!
Programmatically
If now you're along the lines of "ya ya, but still, nibs, ew!" then I'll try to guide you here. A quick caution, it's been a while since I've tried doing this all programmatically so some information might be skewed! With this said let's dive into it!
The least I believe you can get away with is just setting the NSPrincipalClass
key in the Info.plist
to a custom subclass. Then in that subclass override -init
* to set the delegate of the NSApplication
to your custom delegate class. Here your delegate will work as normal and your app should run as expected (thus no nib needed). Note also you can simply subclass NSApplication
and either set itself to be it's own delegate (which from a design POV is a little weird) or not set a delegate and just override certain methods (in some cases calling super). Not instead of using a delegate all together you could instantiate a notification listener object which just listens to notifications like NSApplicationDidFinishLaunchingNotification
and the such.
NSWindowController
A window controller is essentially the base class to a windowed app (sure one can argue otherwise, but this is a pretty good place to start). As you know you just subclass an NSWindowController and then allocate an instance of it and call -showWindow:
.
Nibs
As the documentation states, you don't need a nib to work with an NSWindowController, but "the relationship between a window controller and a nib file is important" (quote from the docs). You simply create a nib that has the NSWindowController
as the owner and then add a window object and assign the window property of the owner to be said window and you're good to go (note setting the delegate of the window to the owner as well is a pretty good idea too - possibly necessary!)! Of course if you check the "Automatically Create Nib for Me" checkbox when creating the subclass this nib will be created for you.
Apple's docs essentially say "use it, it's like electricity; you don't have to use it but it's pretty nice to use".
Programmatically
So you want to be amish eh? (bad joke, sorry). Anywho, to create one of these programmatically you first need to subclass it and then set the window property manually (possibly in -init
) or in the calling function/method.
Why didn't your project work?
Well when you deleted the MainMenu.nib
you deleted the reference to the delegate. Thus your delegate was never allocated and thus is never called for the delegate messages. This is why your window never shows up - your code is never called. If you delete the window object inside the nib but keep the rest of the connections then the code /should/ work (I just tested, then again I have some tweaks to templates and such so my tests might not be accurate).
How to go Fullscreen
Once you create the window controller call -showWindow:
and then [windowController.window toggleFullscreen:nil]
. This should cause the window to go full screen (again tested and works for me).
Creating Art
So you're last question was regarding recreating that (IMO great looking) app. I don't personally have a great tutorial off hand to create a flat mac app, but some general principals to go by that I've learned:
- Lots of Subclasses. In comparison to iOS I've noticed to get a deep level of customization I need to subclass more classes. For example, NSView doesn't inherently have a backgroundColor property, so in order to add this you need to subclass it, add the property, and then implement the
-drawRect:
method to fill with that color (note this isn't the /only/, which brings us to our next point). - Layer Backed or Not? NSViews in Mac apps do not inherently have a CALayer behind the scenes of the view. In order to add layers you need to specify
-setWantsLayer:YES
on the NSView you want a layer on. Note from my experience simply returningYES
from- (BOOL)wantsLayer
in a subclass isn't enough, you need to actually send the message. If you're interested in subclassed NSViews with layers also check out- (BOOL)wantsUpdateLayer
which tells the system to call- (void)updateLayer
instead of- (void)drawrect:(NSRect)r
when redrawing your view. - Patience. I have noticed I could create a fairly decent looking iOS app pretty quickly. Wether this is from experience, the UIKit or some other form of magic is unknown. However, in working with AppKit on creating Mac apps it's taking me significantly longer to create something that looks and feels nice. My belief is that since you have more screen to work with, more precise controls and in general a larger environment to work with, this enables many more options to explore for creating an app. Thus the base application is fairly plain so one can customize it to their heart's desires!
EDIT: Added some links. In general for Mac development don't forget to read this!
*I remember doing it this way in 10.7/8 but have not tried doing it in 10.9
Note to the folks: if you find an error in this post (since it's long I'm assuming I said something silly) please respectfully correct me below. I encourage corrections, however I wish them to be respectful!