Note: Using Objective-C, Cocoa, and Xcode.
At first, I did all my programming in the AppDelegate and had all user-interface elements such as windows in the same '.xib' (nib file). This worked great at first, but then as my application became more advanced with more "features", everything became extremely cluttered and the file too long for my liking.
I'm now trying to progress. I'm wondering how I should (properly and efficiently) go about having a multi-window project? My plan was to have a .xib file for every window, and put only necessary things in the AppDelegate. I would then have a core file for all necessary calculations and such to be used in my application and a Main Controller file to control outlets and actions from all windows in my app. However, I'm quite new to Objective-C and have been running into SO many issues and problems trying to set up Window Controllers and stuff.
Am I even on the right path? Am I doing it wrong? How should I manage a multi-window user-interface application in Xcode?
Thanks in advance.
For multiple windows, I think it's best to use an NSWindowController for each, with it's associated xib file for the window. I'm not sure what you mean by "a Main Controller file to control outlets and actions from all windows" -- each window controller will have outlets to its own window. You can't connect outlets across multiple xib files. You can have one window in the MainMenu.xib file that you get by default when you create a project, and use that to launch other windows perhaps, but it's hard to offer any more specific advice without knowing how all your windows relate to each other.
After Edit:
If you want to open another window, have a menu item's action method be something like this:
self.controller = [[WindowController alloc] initWithWindowNibName:#"WindowController"];
[self.controller showWindow:nil];
Here, I have a property called controller to keep a strong reference to the new window controller. If you don't do that, the controller will be deallocated, so if you have multiple windows, that you want to show at the same time, you'll need properties to hold on to them.
Related
I have a document based app using the standard template. I have two auxiliary panels in Main Menu.xib, and my main logic is currently in the App Delegate, mainly through an IBAction in App Delegate triggered by a button on one of my panels. Everything works fine, but I know it should be organised better.
I have implemented a Preferences panel as suggested by Hillegass in Chapter 12. So:
Create a custom controller called AppController containing instance
of PreferenceController. This is instantiated in Main Menu.xib
Custom PreferenceController class which is subclass of
NSWindowController. This loads the Preferences.xib
Preferences panel created in Preferences.xib
Before I get too far in the app’s development, I want to be sure I’m organising things the right way.
I want to move my main logic out of App Delegate, possibly into App Controller. I want App Controller to be in charge of showing and hiding the various panels, and I want each panel to have its own .xib.
I have created two more subclasses of NSWindowController and made them ivars of the AppController, alongside the PreferencesController eg. Panel1Controller & Panel2Controller.
My problem is that interface builder is not letting me connect an IBAction in AppController to a button on one of my panels. It only lets me connect to the .xib file’s owner, i.e. Panel1Controller in the case of Panel1.xib.
If I put the logic in Panel1Controller, how do I get at one of the other panels (say Panel2Controller?) in order to hide it?
Am I going about this the right way?
Getting very confused….
Any help much appreciated!
Regards,
John
Just for simplicity sake I'd move all the nib elements controlled by the NSWindowController sub-classes out of the main nib and into nibs with the same name as the (NSWindowController) sub-classes that control them. DON'T expose IBOutlets or IBActions in the sub-class headers (they should be in a class extension ("#interface MyWindowController ()") in the source file for that sub-class.
Also, is the AppController a 2nd app delegate? Probably not what you want (there can only be one); you should merge its logic into the existing app delegate if that's the case.
I just came across this method.
This seems to do away with NSWindowController altogether, and make the AppController the file's owner of both .xibs. This way IB allows you to create outlets in AppController for each window, and contain actions.
I have created a very simple, two-window app using this method that hides one window when a button on the other is pressed. Before I go away and re-organise my main app, I want to be sure I'm doing this the correct, standard way, if there is one?
This page contradicts this method, by saying one window = one .xib + one NSWindowController subclass.
If you do it the latter way, how can one window talk to another, when you can't create outlets/actions in the AppController? Actions implemented in a window's NSWindowController class can't see outlets of another window, so how can they communicate?
This seems like pretty standard, basic stuff and yet I cannot find any sources which say which way is correct/best practice.
Another method I have read about here mentions using Notifications.
I'm still wondering though - which is the most common "accepted" method of loading two or more windows in separate .nibs and getting them to talk to each other? I'm surprise this info has been so hard to find.
If you're going to follow this pattern, separate AppDelegate and AppController, then your MainMenu.xib should not contain any window objects of any kind...it should just contain the application menu. Each additional window (NSWindow/NSPanel, etc.) gets its own .xib and its own NSWindowController.
There are two ways to assign references to your properties (IBOutlets) and methods (IBActions): 1) programatically, 2) via Interface Builder. Let's cover the second method!
To be able to wire things up from Interface Builder (IB) you will need a reference to the target object inside IB. For the MainMenu.xib file, this gets setup automatically: the MainMenu.xib contains an "AppDelegate" Object reference. The Object reference exposes the properties and methods in the AppDelegate class that are prefixed by the "IBAction" and "IBOutlet" macros. I write Object (with a capital O) because it is a widget available in the "Object Library" in IB.
You can easily create an instance of a custom objects inside a .xib file (via IB) by dragging an "Object" widget from the Object Library into your .xib. Then set the Object's class to that of your custom class. Once you've done this, the IBActions and IBOutlets in your custom class object will be available in IB. [Note: one thing to remember when doing this, is that when you load the xib, the object will be instantiated automatically. No need to alloc and init from within AppDelegate...you still have to call showWindow: on it].
As you mentioned, another approach is to simply have all of your additional .xib files owned by the AppController. That would be convenient, but it also gets 100% away from the architecture that you were trying to follow in the first place. In other words, if you're going to follow that style, why not just skip the separate AppDelegate and AppController in the first place, and just stick with the former (which would then be a Controller and Delegate).
When you first create an Application in XCode, at it's lowest level it creates the AppDelegate.h/.m and a MainWindow.xib
Once you start modifying the Xib to best suit your design needs - is it best practice to move away from the AppDelegate as the initial Controller or is it always better to create a new WindowController to use with the MainWindow.xib ??
Fritzables
The AppDelegate class cannot be used as a view controller, if that's what you're asking. Your AppDelegate class should be creating an instance of your main View Controller, which loads your main .xib file. Unless you're referring to defining the main Xib in your target configurations, but that's pretty old school.
Are you constrained by the version of iOS you need to support? Because you could avoid using .xib files altogether by using storyboards if you can use iOS 5.0 and up.
Well good question. To me app delegate always has been a initial step to define a project that I create. There are many use for delegates but that is another subject. If you look at the apple doc's related to this subject (over view section) in the following link, there is a great explanation of what is the function of the app delegate there. To answer the question you ask, it is always a good practice to leave the app delegate and create a new controller and execute whatever function you want in there. Here is the link to apple do's and I hope it clears up everything for you my friend. Happy coding.
http://developer.apple.com/library/ios/ipad/#documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html
Is it possible somehow to have different nib files for different OS X versions? Because for example 10.6 does not have auto-layout function, while on 10.7 and 10.8 it is a great function. So if it is possible I could create one nib for 10.6 and another nib for 10.7 and 10.8.
Sure. Just create different NIB files, e.g. add the version at the end of the name of these files, then, at runtime, find out which OS X version the current system is running and programmatically load the right lib for the OS X version.
Doing that for the "main" NIB of your application would be rather difficult, though (it is possible, I have don that before, yet I don't recommend it). Just make your main NIB 10.6 compatible and only place the main menu into it, as well as the application delegate (if you want it to be created by the NIB file instead of creating it programmatically). Place all windows, views and other UI elements into separate NIB files and then within your application delegate, load the NIB files programmatically in a callback like applicationDidFinishLaunching:
Just look at Apple's NIB File Documentation, and search for "Loading Nib Files Programmatically". There are some helpful links to the methods needed for that, as well as some sample code for simple common cases.
Yes, just supply different NIB filenames when loading them. The one place where you can't do this is the application NIB, but it should be free of UI elements beside the menu bar, so just leave it springs-and-struts (ie not autolayout).
This may involve overriding makeWindowControllers or windowNibName on NSDocument, for example, in order to work on the NIB filename. See the documentation for more information about solving this for a particular class - it is definitely doable.
I have been making applications for Mac with Objective-C for about a year now but due to not really understanding how to use classes properly I have only ever used the 'AppDelegate' files. I want to start using classes as soon as possible because from what I understand it's very bad practice to clump it in to one class. Basically, how do I have two windows, each controlled by it's own class. I understand how to make objects similar to NSString or something but don't understand how to have classes that control windows etc.
Thanks
Edit: Basically I want to know how to split up my application in to classes.
If I understand you correctly then you need to create individual controller classes sporting their own IBOutlets and IBActions and hook these up to your UI elements. To split up an existing application into smaller classes requires some knowledge of Object Oriented programming.
Alternatively, you might benefit from reading this (or a similar) book:
'Cocoa Programming for Mac OS X' by Aaron Hillegass.
Try looking for NSWindowController in the docs. You create a custom subclass of NSWindowController and a xib file for it. In the xib file, make sure you set the class on the File's Owner to your custom subclass, and make sure its window outlet is connected to the window in the xib. If all that sounds totally foreign, head for the books! =)
Then, in the code where you want to bring this window onto the screen, you create an instance of your custom subclass and associate it with the xib, like so:
MyCustomWindowController *controller = [[MyCustomWindowController alloc] initWithWindowNibName:#"myxib"]
[controller showWindow:self];
The xib loading system will hook up all your custom outlets and actions on the new controller, and you can show it or do other wonderful NSWindowController things.
For a Mac graphics application (not iPhone), I need something like a main method in Java, the first method that gets a program going. I've been looking at things like NSViewController and NSWindow object. I've looked around but can't find an answer to this seemingly easy question anywhere. (I am very new at this by the way)
Thanks
If you create a Cocoa project from one of the Xcode "Cocoa Application" templates, you'll get a main.m file that includes the usual startup code.
Try working through one of the tutorial projects that you'll find in the documentation.
If you are using the project templates, the startup code is in the ApplicationDelegate file. The main.m, for a Cocoa application, sets up the run loop, runs NSApplication (as you can see in the Info.plist file under the 'Principle class' key.
This then loads the nib file that is specified in the Info.plist file (under the 'Main nib file base name' key). The default is MainMenu.xib. Now have a look at this nib file.
This is already set up by the template to have a 'Files's Owner' of NSApplication (the class that loaded the nib) But There is also a blue block which represents the application delegate. This is already filled out with one delegate method one outlet.
The delegate method is applicationDidFinishLaunching: This method is called by the application. This is only one of the possible delegate methods that it can handle, but it is sent after the run loop is started but before the application receives any events. It is the common place to put your initialisation code. It is in here that you should start to set up your window, which you can get to using the pre-supplied window outlet.
This is just a quick summary. A handy referenece is on Cocoa With Love and Apple's Introduction to Application Architecture document.