|
|
OOFILE | Downloads | Purchasing | Press | Services | Company Information | Soapbox | References | F.A.Q. | HOME |
Intro |
|
This is my learning curve and as a professional developer of 20+ years experience, 10+ on Mac, it shows the issues I dealt with in order. It also reflects a PowerPlant and traditional OO background, unlike many other Cocoa developers. I wrote it to support my learning curve and I like to publish stuff that I think people might find useful. It is not as polished or organised as I'd like but I figured anyone looking for useful stuff and finding this would at least appreciate the information being published and I really don't have the time for the next few months to do more. The main project which was my Cocoa learning curve was a server and information presentation cross-platform tool including drawing OpenGL, histogram and scrolling line graphs using source shared with a Windows and PocketPC version. (Brainwave software now shipped with the personal brainwave devices from http://www.ibva.com/) Please mail corrections, praise, criticism, virtual chocolate: last updated August 7th 2003
|
Important Docs that are hard to findRoles of objects that appear in the Interface Builder window like File's Owner and First Responder are covered under LoadingResources, specifically: file:///Developer/Documentation/Cocoa/TasksAndConcepts/ProgrammingTopics/LoadingResources/index.html
|
Q1 How do I create a new nib file for a new window given an existing project open in Project Builder and a MainMenu.nib open in Interface Builder? A1 To link the new class from its perspective to the nib, if you are subclassing NSDocument, override the method windowNibName returning the nib file name, or in its init use [super initWithNibFileName:@"MyClass"] see Hillegass p 149-152 & Q29
Q2 How do I set Files Owner in a Nib file? A2 Import the class definition (or update it) into the nib file using Classes - Read Files and select the header. Click on Files Owner and choose Tools - Show Info (shift-command-I). Choose Custom Class from the popup at the top of the Files Owner Info panel and scroll down until you see your class. WARNING do NOT double-click on Files Owner, you will be taken to the info for the Class not the Instance, and the Custom Class list will be blank. Now set the reverse - control-drag from the Files Owner to the Window and double-click the 'window' outlet. (Hillegass Figure 7.15, p 150) See: Q7
Q3 Can I check a nib file into CVS? It doesn't have a CVS status in PB. A3
Q4 Is there a convention for the tag names? A4
Q5 Is there a way to see all incoming connections to an object? eg: if I connect the Preferences menu item to an AppController, I can see the connection from the menu item but not the AppController's point of view.- A5
Q6 When do I need to declare a method and when not? A6
Q7 How do I get a non-document window to appear A7 Putting [self showWindow:nil] in the init method also works if you want the window to appear as soon as the instance is created. or 2) in an separate class, an action method that creates it (eg:
AppController in Hillegass p 145) you need to do Note if you mistakenly put something like [self showWindow] in your AppController class you will get a warning from PB that your class doesn't implement the method showWindow. or 3) Apple's SimpleMultiWindow sample:
- (IBAction)displaySecondWindow:(id)sender
{
if (_controller == nil) // Check to see if we've already loaded the second window controller
{
// If not, instantiate a new controller. The controller is told to initWithWindowNibName. The
// new instance will use NSBundle to find and load the correct SecondWindow.nib file. When
// the nib is loaded, the resultField outlet described in SMWSecondWindow.h will be filled.
_controller = [[SMWSecondWindowController alloc] initWithWindowNibName:@"SecondWindow"];
}
//Now that we have a controller, tell it to show the window.
[_controller showWindow:self];
}
Q8 How do I get keydown/up events? A8 Simply provide implementations for -(void)keyDown:(NSEvent*) and -(void)keyUp:(NSEvent*) you can then call [theEvent keyCode] to get the actual key code. Even when a view within your window becomes the FirstResponder, your window controller remains on the responder chain and so, unless a view further down the chain overrides keyUp to swallow the event, you will still have your event method called.
Q9 Why would I draw with NSBezierPath vs NSRectFill, NSLayoutManager etc.? A9 See also Q42 discussion of fast drawing.
Q10 How do I put a view in a window so that the WindowController (ie: Files Owner) of the window can communicate with the custom view? A10
in Interface Builder, to add your custom view to a window
to link the view so it is programmatically addressable from the window controller:
Q11 How do I init an NSString from a wchar_t*? A11 Otherwise, copy your wchar_t array to an array of unichar and use the above or [NSString initWithCharacters:length:]
Q12 How do I add a framework to CodeWarrior (Pro 8)? A12 First make sure it is in the right place - it appears you can only add frameworks from within /System/Libraries/Frameworks or similar dirs, not arbitrary locations.
Q13 What does it mean when my app looks like a folder icon? You need to rebuild your app from scratch with CW? I think it means
that the info.plist didn't get built correctly or that CW failed to
copy an icon (this may be CW Pro 8 bug).
Q14 What does it mean when I get error -39 or just quit when run the debugger (from NSApplicationMain function call) when run from CodeWarrior? A14
Q15 How do I create a Document-based application from a plain App, so it is equivalent to starting from the Document stationery? A15 You will also need to connect up all the traditional File actions to FirstResponder, that's done for you using the Document stationery. Adding actions of your document to be triggered by menu items is a royal pain - you have to copy the actions across and add to the First Responder in the MainMenu.nib Create the Document nib file and set its files Owner to your custom Doc class. Easiest way to copy Actions is to have the Document and MainMenu nib files open so you can see the custom Document class in the Classes tab - using the Attributes tab of the info panel will let you double click an Action and copy its name. Then click on FirstResponder in the MainMenu.nib and Add an action, pasting in the name. This could have been made vastly easier by being able to just drag the Actions across between two info panels but there doesn't seem to be a way to pin panels open.
Q16 How do I get an instance of my Document created at application startup? A16 All other bits from the Document stationery govern behaviour, not whether the Document is loaded.
Q17 If there are multiple Document types, what is instantiated at application startup? A17
Q18 Why would I want a Document subclass? A18
Q19 What do I do differently if I want my Document subclass to just coordinate an in-memory data model, maybe a database connection, but not a local physical file? Put the following in your NSDocument subclass. - (NSData *)dataRepresentationOfType:(NSString *)aType
{
return nil;
}
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
{
return YES;
}
In terms of handling File menu commands, my simple approach
was just to wire the File menu to my own commands but more is likely
required to be scriptable.
Q20 How do I have a Document subclass which doesn't display any windows? A20 Without a nib file and without a method saying to load it (the key point) your Document is still created at application startup (breakpoint in the MyDocument init method to confirm). If there is no current window, an instance of the document is also created every time you activate the application by clicking on it in the Dock.
Q21 Does the Cocoa Stationery for CW Pro 8 create a Document-based App? A21
Q22 Where are the plist entries stored that are visible in ProjectBuilder's Target panes? A22
Q23 How do I convert property list items such as in a .pbxproj file to the format CodeWarrior uses? A23 ** remember to force recompiles of property list files!
Q23 My init method in a window controller isn't being called even though my Document is creating it in the makeWindowControllers method. A23
Q24 I have a standard data file being read in a portable C++ program, how do I trigger it being read in the NSDocument? A24
Q25 What is the Cocoa Equivalent of an MFC OnUpdateBlah(CCmdUI* pCmdUI) or PowerPlant FindCommandStatus? A25 This means you need to implement a method with series of if-then actions for everything handled by that target, which is similar to FindCommandStatus:. - (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem {
Q26 How do I get actions into the First Responder. A26 Easiest way to copy Actions is to have the Document and MainMenu nib files open so you can see the custom Document class in the Classes tab - using the Attributes tab of the info panel will let you double click an Action and copy its name. Then click on FirstResponder in the MainMenu.nib and Add an action, pasting in the name. This could have been made vastly easier by being able to just drag the Actions across between two info panels but there doesn't seem to be a way to pin panels open. Metrowerks wouldn't have left the tool like this!
Q27 How do I get an Image into the Images tab of a nib? A27
Q28 Should I subclass NSWindow? A28
Q29 Should I subclass NSWindowController? Q29 if you want to call windowDidLoad, windowWillLoad or windowTitleForDocumentDisplayName. When you have a second window type in an app, create a controller. http://www.bignerdranch.com/Book/ 1. I create a nib file for each window in my application. Then I go through the nib files creating a controller for each using the following guidelines. 2. MainMenu.nib? I create a subclass of NSObject called AppController and instantiate it. AppController is made the delegate of the NSApplication. 3. A document nib? I create a subclass of NSDocument and set the "File's Owner" to be that type. 4. Other window (Example: a find panel)? I create a subclass of NSWindowController and set the "File's Owner" to be that type. It is OK to wander from this. Sometimes I will put multiple windows in a single nib. Other times, I will have multiple controllers for a single window. This rule is just a good starting point.
Q30 How does MVC (Model-View-Controller) apply to Cocoa? A30
Q31 If I add actions and outlets in Interface Builder to a class I've already written, how do I get them back into that class? A31
Q32 When I run my Document-based app I get an alert saying can't create new docment - why? A32
Q33 When is a return value autoreleased? A33
Q34 Why can't I reconnect the wiring from a menu to actions - I know the actions are there on my destination object? A34
Q35 What's a quick way to remove connections? A35
Q36 How do I get my icon in CodeWarrior 8.3?
Original tip: to avoid problems with CodeWarrior finding files in your generated app bundles, make sure your English.lproj directory is the FIRST in your access path. I tried turning off recursive access paths for the project directory but that breaks the build. Better idea: Set your output directory to a bracketed subdir like (CWBuild).
Q37 How do you create a simple Alert, like ShowAlert? A37 For an even simpler message box, use NSRunAlertPanel.
eg:
NSRunAlertPanel(@"Unable to save preferences to file", newName, nil, nil, nil);
Q38 How do you create a sheet with data entry? A38 Data comes back out of the sheet by having two NSTextField*'s wired to outlets in the document. The sheet is invoked with [
Q39 How do you create a floating palette for data entry? A39 Subclass NSWindowController to manage it. Add outlets to your window controller for the controls which you want to set or get data back from. Then instantiate your window controller somewhere in an action responding to a Show method. This might be in your App, Document or related class. The Sketch sample code has a nice approach that uses lazy loading by SKTDrawAppDelegate being responsible for all these commands, and invoking panels:
Q40 How do you get the window created by NSBundle loadNibNamed:
...
A40
Q41 How do you get a nib back out of a built app? A41
cd blah.app/Contents/Resources
cp -Rp MainMenu.nib /Users/andydent/dev/blah/English.lproj
Q42 What's the fastest way to draw, eg: quickly refreshing a graph with text and lots of lines/rectangles? A42 I had a lot of trouble finding clear answers to this issue but it seems that otherwise text rendering is very sophisticated/heavyweight. Eric Pepke's Mac One-Liner news postings suggested using Quickdraw and my experience in graphing seems to bear it out. It is possible that OpenGL drawing may be faster but drawing text in OpenGL on OSX at present is very painful.
Q43 Why do I sometimes crash when I've got a pointer back from a newly created window? Painfully-learned lesson <insert scream of realization/triumph>. Window initialisation appears to be multi-threaded. If the pointer you get back is not created in init then it will not necessarily be valid if you retrieve it immediately after window construction. MyWindowController* _controller = [[MyWindowController alloc] initWithWindowNibName:@"MFOSXGraphFFT"]; return [_controller settings]; // unsafe! What makes that unsafe is that settings returns a variable initialised in a deferred manner: @implementation MyWindowController
- (void)windowDidLoad
{
mSettings = [mGraphView getSettings];
} Later insight - I don't think they are multi-threading here, at least not in your app. What gives the appearance of multi-threading is lazy construction. If you call [myWindowController window] then you force your window nib to be loaded and windowDidLoad will be invoked. Otherwise you are waiting for Aqua to do a refresh and instantiate that window. eg: here's a piece of code that retains a window list for later closing a subset of windows. - (void) FinishCreatingGraphWindow:(MFGraphWindow*)inController
{
mGraphWindows.push_back(inController);
NSWindow* pWindow = [inController window]; // force windowDidLoad to be called
NSCAssert(pWindow!=nil, @"Should have a window so we can listen for it closing");
[ [NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(notifyGraphWindowClosed:)
name:@"NSWindowWillCloseNotification"
object:pWindow
];
}
Q44 How do I setup my CodeWarrior precompiled headers to cope with both .mm and .m files? If your project has both .m and .mm files you can't have just one precompiled header, you have to have two because one is Objective-C and the other is Objective-C++ and compiles C++ code and applies C++ mangling to other functions (unless they are inside extern "C").
MyPrefix.h
// put MyPrefix.h in the CodeWarrior C++ settings Prefix File
#if __option (dont_inline) // assume good indicator of debug mode
#ifdef __cplusplus
#include "./Debug MyCocoaHeaders++Mach-O"
#else
#include "./Debug MyCocoaHeadersMach-O"
#endif
#else // non-debug
#ifdef __cplusplus
#include "./MyCocoaHeaders++Mach-O"
#else
#include "./MyCocoaHeadersMach-O"
#endif
#endif
MyCocoaHeaders.pchmm#if __option (dont_inline) #pragma precompile_target "Debug MyCocoaHeadersMach-O" #else #pragma precompile_target "MyCocoaHeadersMach-O" #endif #include "MyCommonHeader.h"
MyCocoaHeaders.pchmm#if __option (dont_inline) #pragma precompile_target "Debug MyCocoaHeaders++Mach-O" #else #pragma precompile_target "MyCocoaHeaders++Mach-O" #endif #include "MyCommonHeader.h"
MyCommonHeader.h#if __option (dont_inline) #define DEBUG 1 // our flag, standard in Windows code, also used in OSX CarbonCore/Debugging.h #else #define NDEBUG // disabled assert() #endif #pragma c99 on #define _MSL_USING_MW_C_HEADERS 1 #pragma once on #include "CocoaHeaders.m" // standard MW header
Q45 Why doesn't my Window appear after editing the nib file? Check that the Files Owner is still an NSWindowController subclass and that it has the window outlet connected. One scenario in which I managed to break this connection was where my subclass had been refactored so I had another class between it an NSWindowController. Importing the new definition broke the connection and even after the new parent was imported, so Interface Builder knew that my Files Owner was an NSWindowController, it didn't reconnect. There is a warning dialog when you read files in this kind of circumstance - take it seriously and check all connections after. I ignored the warning because I thought it was just about changes to Actions.
Q46 How do I check what modifier keys are down? The old-fashioned way still works:
#include <carbon/carbon.h> // for GetKeys - implies need Carbon framework as well ... KeyMap theKeys; ::GetKeys(theKeys); const bool shiftDown = theKeys[1] & kShiftKey; const bool ctlDown = theKeys[1] & kControlKey; const bool optionDown = theKeys[1] & kOptionKey;
Q47 How do I have a menu item which changes title according to the modifier keys, eg: close / close all? Respond to the state of the modifier keys in the validateMenuItem, eg:
#include <carbon/carbon.h> // for GetKeys - implies need Carbon framework as well
...
- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
{
SEL theAction = [menuItem action];
if (theAction == @selector(OnFileClose:)) {
KeyMap theKeys;
::GetKeys(theKeys);
if (theKeys[1] & kOptionKey) {
[menuItem setTitle:@"Close all Graphs"];
}
else {
[menuItem setTitle:@"Close"];
}
}
else if (theAction == ...
// default - call parent
else
return YESem];
return YES; // all the cases above are enabled unless they explicitly return
}
PowerPlant vs CocoaCocoa minuses:
Cocoa pluses:
CodeWarrior vs ProjectBuilderThis is Pro9 (Pro 8 plus publicly disclosed info in the newsgroups) vs PB 2.1 (Dec 2002 dev tools) PB lacks
PB benefits
|