These are the problems and solutions that I discovered while writing my first Cocoa program for Mac

Newbie FAQ:
-----------
** Basic Questions **
1. What books do you recommend?
2. I'm stuck! Where do I go for more information?
3. Can I use my C++ classes in my Cocoa interface?
4. Why do I get " illegal expression, found `unknown' " in perfectly good
code?
5. What's wrong with the documentation?  This thing should be documented,
but I can't find it! (NSString, NSAttributedString, NSRect, etc.)
** Objective C and Memory management **
6. Memory Management with Objective-C really gives me the willies.
7. What does it mean when I get an EXC_BAD_ACCESS error?
8. How do I check to see if I am leaking memory in Objective-C?
9. Why doesn't the object I instantiated with InterfaceBuilder
ever receive a "dealloc" ?
10. I get malloc()/free() problems with
[NSData initWithBytesNoCopy: length:]
11. Can you have static variables inside an objective-C classes?
OR: How do I share objects across my application?
** Interface Builder **
12. Interface Builder can no longer open my NIB file. Help!
13. Why don't my delegated functions get called?  Why don't my controls do
anything?
** Controls and Buttons **
14. How do I hide controls?
15. I don't get how the springs work in the Interface-Builder control
properties.
16. How do I set the focus to a control?
17. How do I set the "default button"
(activated when the user presses RETURN), OR:
Why are buttons which were formerly default turning dark grey?
** Sheets and Alerts **
18. How do I display a simple MessageBox() / Alert Box ?
19. Why doesn't the Default button pulse when I use NSRunAlertPanel() ?
20. How do I show a sheet when my document window is first displayed ?
21. How do I show a sheet when my application is first launched ?
22. Why am I having problems using runModalForWindow with sheets?
** Drawing Strings **
23. How do I get the size of text if it were to be drawn on the screen?
24. How do I draw strings with different attributes (font, color,
underline, italics, etc)?
** NSTextField and NSTextView **
25. How do I insert/append text into an NSTextField or NSTextView ?
26. Why can't I create a formatter to catch an empty NSTextField?
27. Why does my "textDidChange" method never get called for my NSTextField?
28. How do I get an NSTableView to do something on a double-click?
29. How do I disable an NSTableView?
[myTable setEnabled:NO] doesn't seem to do anything.
** Folders and files in your Application **
30. How do I put a folder of files into my application?
31. And then how do I get access to them?
** Web sites, HTTP, and URLs ***
32. What is an informal protocol?
33. What is the NSURLClient informal protocol? How do I download a URL in
the background?
34. How do I cancel an [NSURL loadResourceDataNotifyingClient:] in
progress?
35. How do I launch a web browser to a URL?
** On-Line Help **
36. How do I add on-line help to my application?
37. How do I jump to a specific page of my on-line help?
** NSScanner **
38. Why can't I scan past newline or whitespace with NSScanner?
1. What books do you recommend?
--------------------------------
I learned Cocoa and Objective-C using "Cocoa Programming for Mac OS X" by
Aaron Hillegass, and other Cocoa programmers seem to like it too.
Also try the books listed here:
http://www.cocoadev.com/index.pl?CocoaBooks
The O'Reily book is also recommended.
2. I'm stuck!  Where do I go for more information?
---------------------------------------------------
Start here:  http://cocoa.mamasam.com/
It is a **searchable** archive of the Cocoa Developer's mailing list.
Probably your issue has already been discussed on this list somewhere. A
good way to start is to search for the NS-class that you are having
trouble with.
If you want to become a member of the community (or just lurk), join
Apple's Cocoa Developers mailing list:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Ohter useful sites:
http://www.cocoadev.com/
http://www.cocoadevcentral.com/
http://www.macdevcenter.com/
3.  Can I use my C++ classes in my Cocoa interface?
----------------------------------------------------
Yes!  It works great.
Click here for more information on Objective-C++.
4. Why do I get "illegal expression, found `unknown' " in perfectly good
code?
--------------------------------------------------------------------------
Probably because you cut'd and paste'd the code from someplace with weird
end-of-line characters or other control characters. Try selecting the
white-space from just after the last character on the offending line to
just before the first character on the next line, and then hit
CARRIAGE-RETURN.
If that doesn't work, try the pull-down menu:  Format/Show Control
Characters.
5. What's wrong with the documentation?  This thing should be documented,
but I can't find it! (NSString, NSAttributedString, NSRect, etc.)
--------------------------------------------------------------------------
The documentation does have a few eccentricities:
A. Some classes are documented in both the Foundation Classes and the
Application classes documentation. These include:
NSAttributedString, NSBundle, NSCoder, NSString,
NSMutableAttributedString, NSURL
So, if you happend to be browsing the Application Classes documentation,
you will discover that these classes appear to be weirdly empty (for
example, no "length" method in NSAttributedString).
B. Many useful structures and functions are "hidden" at the bottom of the
Foundation classes API reference.


Click here and then scroll to the bottom of the page.
and click on "Functions" where you will find many useful C functions (not
methods), such as routines for NSDecimal, NSLog(), User names, Home
directories,  temporary directories, NSPoint manipulations, NSRanges (e.g.
NSMakeRange()),  NSRect manipulations, etc.
Also at the end of the Foundation API reference you will find "Types and
Constants". This is where most of the smaller C-structures are located,
such as:  NSRect, NSRange, NSDecimal, NSPoint, and NSSize. Note that these
are structures and *not* Objective-C objects.
C. There are also useful functions / structures hidden at the bottom of
the APPKit reference:


Click here and then scroll to the bottom of the page.
This includes useful functions such as counting windows, NSRunAlertPanel,
NSBeep(), accessibility functions, etc.
General Point:  If you are frustrated with the documentation, take a step
back and look at which API reference you are browsing.
6. Memory Management with Objective-C really gives me the willies.
-------------------------------------------------------------------
You will be O.K. if you just keep these 'rules of thumb' in mind:
A.  If you got the object with "alloc" then be sure to "release" it:
myobject = [[MyObject alloc] init];
// "alloc" has already done a "retain" on the object
.
.
.
- (void)dealloc {
[myobject release];
[super dealloc];
}
B. If you got the object with "alloc" and only need it temporarily, then
just "autorelease" it:
myobject = [[[MyObject alloc] init] autorelease];
// myobject will be automatically released when this itteration
//    of the event loop is complete
C. If you got the object any other way (i.e. with one of the "+" calls),
then the object has already been autoreleased.
So, if the object is only used temporarily, then do nothing. It will be
released automatically when the current itteration of the event loop is
finished.
If you want to keep the object, then be sure to "retain" it, and then
"release" it in your dealloc method. For example:
NSString *myString = [NSString stringWithString:@"Hello World!"];
// myString is AutoReleased by "stringWithString" and must be retained
//   if I plan to use it for a while:
[myString retain];
.
.
.
- (void)dealloc {
[myString release];
[super dealloc];
}
D. If you do a ton of stuff between event loops, it's possible that your
application will use up a lot of memory from objects being "autoreleased"
without actually being released. If this is the case, you should probably
create your own autorelease pool, and then release it every so often.
Click here for more details on creating your own autorelease pools.
E. Don't forget to put a [super dealloc]; at the end of all your - dealloc
routines!
7. What does it mean when I get an EXC_BAD_ACCESS error?
---------------------------------------------------------
Probably you are using a bad pointer to an object. This is typically
because:
A.  The object it once referred to has already been released:
MyObject *myObject = [[MyObject alloc] init];
.
.
.
[myObject release];
.
.
.
[myObject myMethod];   // << Causes BAD_ACCESS error
B. You have multiply autoreleased your object:
MyObject *myObject = [[MyObject alloc] init];
[myObject autorelease];
.
.
.
[myObject autorelease];
.
.
.
// when the autorelease pool is cleaned up, your program will crash
// with a BAD_ACCESS error, because it will attempt to release your
// object twice
C. You are dereferencing a bad 'C' pointer:
myPtr = NULL;
abc = myPtr->bTest;  // << Causes BAD_ACCESS error
Note that, unlike 'C', Objective C does allow you to send methods to 'nil'
receivers:
myObject = nil;
[myObject doStuff];  // << No error, rather it is just ignored
8.  How do I check to see if I am leaking memory?
--------------------------------------------------
You could try using "ObjectAlloc" (an application delivered with the
toolkit). This procedure seems to work fairly well:
a.  Launch your application with ObjectAlloc
b.  Run it for a while to "warm up" your memory.
c.  Swich back and forth between your application and the finder a few
times
d.  Now, click on the X:  all of the "current" settings go to zero.
e.  Execute the questionable code 5 times.
f.  Now click on "Show since mark"
g.  Look for allocations which are multiples of 5 and investigate them by
looking at the Instance Browser or the Call Stacks.
Warning:  The foundation and application toolkits do lots of caching, and
lots of times things are not released (as far as I can tell, example:
bundles). So, don't sweat every little byte, especially when it is not
allocated by your code.
9.  Why doesn't the object I instantiated with InterfaceBuilder
never receive a "dealloc" ?
----------------------------------------------------------------
I don't know.  Probably you need to manually alloc, init, and dealloc your
object with some sort of controller class, such as an application
controller object.
Bundles don't appear to be unloaded when you quit your program. If you
have cleanup that you need to do on program termination, then have your
obect listen for NSApplicationWillTerminateNotification (see #11 for an
example).
10. I get malloc()/free() problems with
[NSData initWithBytesNoCopy: length:]
-----------------------------------------
Yes you do. The "bytes" field will be automatically free'd when using
these routines:
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length;
- (id)initWithBytesNoCopy:(void *)bytes length:(unsigned)length;
A future version of Cocoa (Jaguar?) will contain additional flags to
control this behaviour:
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length
freeWhenDone:(BOOL)b;
- (id)initWithBytesNoCopy:(void *)bytes length:(unsigned)length
freeWhenDone:(BOOL)b;
Until then, just use "initWithBytes".
11. Can you have static variables inside an objective-C classes?
OR: How do I share objects across my application?
----------------------------------------------------------------
Basically, the best solution seems to be to define this global data as
part of your @implementation, rather than inside the @interface.
Here's a fairly complete solution, where the shared object is an instance
of the "MyObject" class:
A. Add the following method declarations to your MyObject.h @interface:
+ (MyObject *)getSharedObject();
- (void)doTerminate:(NSNotification *)note;
B. Add the following to your MyObject.m @implementation:
static MyObject *sharedMyObject = nil;  // Points to the shared object
+ (MyObject *)getSharedObject()
{
if(sharedMyObject == nil) {
sharedMyObject = [[MyObject alloc] init];
// Do other initializations of sharedMyObject here, as necessary
[[NSNotificationCenter defaultCenter]
addObserver:sharedMyObject
selector:@selector(doTerminate:)
name:NSApplicationWillTerminateNotification
object:NSApp];
}
return sharedMyObject;
}
- (void)doTerminate:(NSNotification *)note
{
if(sharedMyObject) {
[[NSNotificationCenter defaultCenter] removeObserver:sharedMyObject];
// Do other cleanup on sharedMyObject here as necessary
[sharedMyObject release];
}
}
Note:  If you don't care that your shared object is released at program
termination (or if you have other ways to properly release/dealloc it),
then remove the NSNotificationCenter lines and the [ doTerminate:] method.
12. Interface Builder can no longer open my NIB file. Help!
-----------------------------------------------------------
Did you recently delete or move things in your project? Move them back and
then try opening your NIB file. For example, this will happen when the NIB
contains an image, and you have moved the image (or moved it to the
trash).
13. Why don't my delegated functions get called?  Why don't my controls do
anything?
--------------------------------------------------------------------------
This can happen if you've made changes to your class include file, but
have not propagated those changes into your NIB. Open up the NIB for your
window, double click on "File's Owner", then execute the pull-down
command:  Classes / Read MyClass.h.
And then go and verify all your connections.
14. How do I hide controls?
---------------------------
Generally, your program will conform better with the OS X user interface
guidelines if it disables the control rather than hiding it.
However, if you really wish to hide a control, know that there is no
"setVisible" method or attribute. Instead, you will need to remove the
control with [myControl removeFromSuperview] to hide it and then add it
back with [superviewOfMyControl addSubview]. There are some other issues,
such as resizing and tab-order which you may also need to worry about.
Here is one possible solution.
A.  Add the following files to your project:
----- ControlHider.h -----
#import <AppKit/AppKit.h>
@interface ControlHider : NSObject {
NSView *childView;      // The child which is currently hidden
NSSize oldFrameSize;    // The parent's size when the control was
removed
NSView *parentView;     // parent in which which held the child
NSView *previousKeyView;  // Pointer to child's previous key view
}
- (void)hideControl:(NSView *)child;
- (void)showControl;
@end
----- ControlHider.m -----
#import "ControlHider.h"
@implementation ControlHider
- (id)init
{
if(self = [super init]) {
childView = nil;
oldFrameSize.width = 0.0;
oldFrameSize.height = 0.0;
parentView = nil;
previousKeyView = nil;
}
return self;
}
- (void)hideControl:(NSView *)child
{
NSRect rectParent = [[child superview] frame];
childView = child;
parentView = [child superview];
oldFrameSize = rectParent.size;
previousKeyView = [child previousKeyView];
[child retain];
[child removeFromSuperview];
}
- (void)showControl
{
if(parentView == nil)  return;
[parentView addSubview:childView];
[previousKeyView setNextKeyView:childView];
[childView resizeWithOldSuperviewSize:oldFrameSize];
[childView release];
parentView = nil;
}
@end
----- end file -----
B.  For each control which you wish to hide, add the following to your
".h" file:
IBOutlet NSTextField *myControl;  // the control to hide
ControlHider *hiderMyControl;     // << Add this line for each control
C.  In your "init" routine, allocate the ControlHider(s):
hiderMyControl = [[ControlHider alloc] init];
D.  In your "dealloc" routine, release the ControlHider(s):
[hiderMyControl release];
E.  Whenever you need to hide your control, do the following:
[hiderMyControl hideControl:myControl];
F.  Whenever you need to show your control, do the following:
[hiderMyControl showControl];
Note:  Do not "show" the control twice in a row.
15. I don't get how the springs work in the Interface-Builder control
properties.
---------------------------------------------------------------------
Actually, they're really cool, but they do seem to have a problem with
controls which are outside their superview.
Click on a control in the Interface-Builder, open up the properties
(SHIFT-COMMAND-i), and then choose the "Size" pull-down menu, you will see
a box called "Autosizing" (referred to as "Autoresizing" in the API
documentation).
Inside the "Autosizing" box there is a nested box and a bunch of
double-lines. Click on a double line and it turns into a spring.
There are six springs:
A.  Four springs between the edges of inner and outer box
These affect the position of the box within its super view.
The super view can either be the window/panel which holds the control or
some nested view (such as an NSTabView or NSBox), which in turn holds the
control.
* When the spring is absent (i.e. just a straight line), the control will
be "fixed" to the specified edge. This means that even if the view is
resized, the control will maintain the same distance between itself and
the fixed edge.
* If there is a conflict, e.g. the control is fixed on all four sides and
its superview changes size, the control will default to being fixed to the
left and *bottom* of the view.
B.  Two springs inside the inner box (horizontal and vertical)
These affect the size of the control as its super view is resized.
*  When a spring is absent, then the control will maintain a fixed size
(either horizontal, vertical, or both) no matter how the superview is
resized.
*  When a spring is present, the size of the control changes as the size
of the superview changes.
OK, so I don't understand how controls which are outside their superview
work. In the Interface Builder you can drag a control outside it's
superview (or expand the superview, add the control, and then collapse the
superview). When automatically resizing the window with [myWindow
setFrame:newFrame display:YES animate:YES] it seems that the control
automatically "pops" into view before being moved down. Others have
reported that similar strange things happen when drawers open up from the
sides of windows. However, it seems to work OK when the user manually
resizes the window.
I guess just watch out when you have controls that are outside of their
superview. Sometimes the behaviour will be strange and you may need to
move the controls yourself with [myControl setFrame:newFrame] and
[myControl dispay] commands.
16. How do I set the focus to a control?
----------------------------------------
Use "makeFirstResponder":
[myWindow makeFirstResponder:myControl];
17. How do I set the "default button"
(activated when the user presses RETURN),  OR:
My buttons which used to be default turn dark grey.
----------------------------------------------------------------------
There are two ways to set the "default" button,
A. [myButton setKeyEquivalent:@"\r"];
B. [myWindow setDefaultButtonCell:[myButton cell]];
Version 'B' is better, since, with Version A, if you turn off the
keyEquivalent (i.e. set it to the empty string or something) then the
button will end up a dark-grey color, depending on how dark the blue
pulsing was when the switch occurred.
18.  How do I display a simple MessageBox() / Alert Box ?
---------------------------------------------------------
Use NSRunAlertPanel() or NSRunCriticalAlertPanel(). See:
Click here for more information on NSRunAlertPanel()
For example:
int iResponse =
NSRunAlertPanel(@"Close Document",
@"Are you sure you want to close the document?",
@"OK", @"Cancel", /*ThirdButtonHere:*/nil
/*, args for a printf-style msg go here */);
switch(iResponse) {
case NSAlertDefaultReturn:    /* user pressed OK */
break;
case NSAlertAlternateReturn:  /* user pressed Cancel */
break;
case NSAlertOtherReturn:      /* user pressed the third button */
break;
case NSAlertErrorReturn:      /* an error occurred */
break;
}
If your application is not currently the foremost application, the user
will be notified (bouncing icon) that it has posted an alert and requires
attention.
19.  Why doesn't the Default button pulse when I use NSRunAlertPanel() ?
------------------------------------------------------------------------
This is a known bug.
20.  How do I show a sheet when my document window is first displayed ?
-----------------------------------------------------------------------
The problem here is that your application must be fully initialized before
you can run your own sheets.  Therefore, you can not launch your panel in
[NSDocument windowControllerDidLoadNib:...] or [object awakeFromNib:...]
or similar methods because the application is not fully initialized yet.
If the application is not fully initialized, some weird things can happen,
such as missing controls, strange titles, etc. Also, you may notice that
your sheet looks fine (and works fine, too), but takes an extra click to
activate it (i.e. an extra click before it becomes the "key" window).
So, one possible solution is to send a message the first time the window
becomes Key (because that's when the window object is available), and to
use the [NSRunLoop performSelector:] method to delay the message by one
event loop (gives the application time to start up).
Steps to implement this solution:
A.  Have one of your objects become the delegate of the first window which
is launched. For example, your NSDocument could be the delegate for your
main document window.
B.  Then, handle the "windowDidBecomeKey" delegated function. This will be
called after the document window is popped to the front:
- (void)windowDidBecomeKey:(NSNotification *)aNotification
{
if(!mySheetController) {
// only executed the first time the window becomes Key
mySheetController = [[MySheetController alloc] init];
[mySheetController setDocWindow:window];
//* Hack to send startup sheet when application is ready
[[NSRunLoop currentRunLoop]
performSelector:@selector(showWindow:)
target:mySheetController argument:self order:0
modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
}
}
C.  Then, add the following code to your "MySheetController" object (the
object which handles the buttons on your sheet):
// Set the parent window so it can be used in [NSApp beginSheet:...]
- (void)setDocWindow:(NSWindow *)window
{
docWindow = window;
}
- (IBAction)showWindow:(id)sender
{
// Perform initializations on your sheet needed before it is displayed
.
.
.
[NSApp beginSheet:panelChoose
modalForWindow:docWindow
modalDelegate:sender
didEndSelector:@selector(myDidEnd:returnCode:contextInfo:)
contextInfo:nil];
}
D. Notice that "NSApp beginSheet" has specified a modalDelegate: which
will receive the "DidEnd:" message when the sheet is closed. To handle
this be sure to add the following method to the object which is the
delegatee:
- (IBAction)myDidEnd:(NSWindow *)sheet returnCode:(int)returnCode
contextInfo:(void *)contextInfo
{
if(returnCode)
/* Do something if the user clicked OK */;
else
/* Do something if the user clicked Cancel */;
}
E. Don't forget to shut down your sheet properly with [NSApp
endSheet:returnCode:] (see #22 below).
21.  How do I show a sheet when my application is first launched ?
------------------------------------------------------------------
If you want to display a sheet whenever you open a new document, then use
#20 above. If you only need to show the sheet once when your program is
launched, then register your object to listen for
NSApplicationDidFinishLaunchingNotification and display the sheet there.
22. Why am I having problems using runModalForWindow with sheets?
-----------------------------------------------------------------
Because runModalForWindow is "application modal", not just "window modal".
Application modal means that the dialog takes over application event
processing, so none of the application menus work, window events don't get
processed, etc.
Window modal continues all standard event processing. You can open new
documents and use the other menu commands in your application. The only
difference, really, is that events to the window will be sent to the sheet
instead of the window.
Sheets are intended to be Window Modal, not application modal, and so they
don't work well with "runModalForWindow". If you do, then strange things
may happen with your window, such as the default button not throbbing or
(as has been reported a ways back) old sheets previously displayed may
magically pop back up.
Suppose this is what you had in your code:
- (void)doMySheet
{
int response;
[NSApp beginSheet:mySheetWindow
modalForWindow:myDocumentWindow
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
// >>> Wrong: don't use runModalForWindow with sheets <<<
response = [NSApp runModalForWindow:mySheetWindow];
[NSApp endSheet:mySheetWindow];
[mySheetWindow orderOut:self];
if(response == 0)  return;
// continue with application
}
Change it to this:
- (void)doMySheet
{
int response;
[NSApp beginSheet:mySheetWindow
modalForWindow:myDocumentWindow
modalDelegate:self
didEndSelector:@selector(mySheetDidEnd:returnCode:contextInfo:)
contextInfo: nil];
return;  // leave without doing anything else
}
- (IBAction) mySheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode
contextInfo:(void *)contextInfo
[sheet orderOut:self];
if(returnCode == 0)  return;
// continue with application
}
(I know: if you have a lot of local variables in doMySheet it's a pain,
sorry 'bout that)
And don't forget to do these things as well:
A. Be sure to declare mySheetDidEnd in the appropriate .h file
B. Instead of using [NSApp stopModal] to end your dialog, use
[NSApp endSheet:window returnCode:1];
23.  How do I get the size of text when it is drawn on the screen?
------------------------------------------------------------------
First, you need to get an "attributed" string (see NSAttributedString in
the application toolkit), then you can call [myAttributedString size] to
get the height and width of the string as drawn on the screen.
You can get an attributed string directly from controls with [myControl
attributedStringValue].
An "AttributedString" is simply a string with a set of attributes.
Attributes are stored in a dictionary (NSDictionary or
NSMutableDictionary). Attributes are name/value pairs stored in the
dictionary which specify things like Font Name, Underlined, Foreground
Color, Background Color, etc.
So, to create your own attributed strings, you need to create a dictionary
with name/value pairs for your text attributes.
A list of constants for possible attribute names (such as
NSFontAttributeName) can be found here:
A href="http://developer.apple.com/techpubs/macosx/Cocoa/Reference/ApplicationKit/ObjC_classic/Classes/NSAttributedString.html">Click here for constants and attribute names.
To create a dictionary:  (example)
NSDictionary attribDict =
[NSDictionary dictionaryWithObjectsAndKeys:
          [NSColor redColor], NSForegroundColorAttributeName,
          [NSFont systemFontOfSize:24], NSFontAttributeName,
nil];
To create the attributed string:
NSAttributedString *attribString =
[[NSAttributedString alloc] initWithString:@"This is my string"
attributes:attribDict];
And now you can get the size of the string with:
NSSize textSize = [attribString size];
24.  How do I draw strings with different attributes (font, color,
underline, italics, etc)?
-------------------------------------------------------------------
See #23 above about creating attributed strings.
Once you have an attributed string, use the "drawAtPoint" and "drawInRect"
methods to draw the string.
25. How do I insert/append text into an NSTextField or NSTextView ?
-------------------------------------------------------------------
If you want to replace the currently selected text of an NSTextField, OR
insert text at the current insertion point, use the following code:
NSText *textEditor = [myTextField currentEditor];
[textEditor replaceCharactersInRange:[textEditor selectedRange]
withString:@"hello world"];
The deal here is that NSTextField shares the NSText object with all of the
other NSTextField's in your window, so you need to get the current editor
first and then you can send it commands to modify the contents of your
NSTextView.
Also note that this will not work unless the control is currently active
(i.e. the first responder, see #16).
If you want to insert text into an NSTextField without making it active,
you could:
A.  Get the text, with [myControl stringValue], append to the string, and
then set it again with [myControl setStringValue].
B.  Change the NSTextField to an NSTextView, and then modify the
NSTextStorage of the NSTextView directly. For example:
// This will append text to the end of an NSTextView:
NSRange myRange = NSMakeRange([[myTextView textStorage] length], 0);
[[myTextView textStorage] replaceCharactersInRange:myRange
withString:@"Text to append to the end"];
By doing this you will be allocating a dedicated text editor to your
field.
26. Why can't I create a formatter to catch an empty NSTextField?
-----------------------------------------------------------------
Because the formatter does not get called when the user attempts to leave
a blank NSTextField.
Instead, add this delegate function to the object which manages your
window:
- (BOOL)control:(NSControl *)control
textShouldEndEditing:(NSText *)fieldEditor
{
if (control == myControl) {
if ([[fieldEditor string] isEqualToString:@""]) {
NSBeep();
return NO;
}
}
return YES;
}
... and make sure to make the object the delegate of your *control* in
Interface Builder.
If the field is blank, the user can enter it and leave it at will (i.e.
with TAB or mouse clicks or whatever). However, if the user enters
something, clears it, and then attempts to leave, this function will be
called and will cause a system beep.
I have not yet figured out how to prohibit the user from leaving an empty
NSTextField if they jump into it and then jump right out. Maybe that's not
a good user interface technique anyway.
Helpful hint:  Instead of beeping when the user enters an empty text
field, why not just enable/disable the OK button? In other words, disable
the OK button until all of your requred Text Fields have been entered. You
can trap the delegated method "controlTextDidChange", then see if all
required fields have something in them, and then enable/disable the OK
button.
27. Why does my "textDidChange" method never get called for my
NSTextField?
---------------------------------------------------------------------------
Three possible reasons:
A.  It should be "controlTextDidChange" (see NSControl), not simply
"textDidChange".
B.  You forgot to make your object (i.e. the object in which
controlTextDidChange is defined) the delegate for your control in the
Interface Builder.
C.  See #13.
28. How do I get an NSTableView to do something on a double-click?
------------------------------------------------------------------
Put this code someplace where you do initializations:
[myTableView setTarget:myWindowController];
[myTableView setDoubleAction:@selector(doDoubleClick:)];
And add this function to your .m file (don't forget to declare it in your
.h include file):
- (IBAction)doDoubleClick:(id)sender
{
// Do what you want to do, sender points to the table view
}
29. How do I disable an NSTableView?
[myTable setEnabled:NO] doesn't seem to do anything.
--------------------------------------------------------
NSTableView does not appear to respond to setEnabled:. The following code
is a sub-class of NSTableView which adds enabling and disabling.
Steps:
A.  Add the following files to your project:
----- ExtendedTableView.h -----
#import <AppKit/AppKit.h>
@interface ExtendedTableView : NSTableView {
NSMutableArray *saveTextColors;
NSMutableArray *saveBackgroundColors;
BOOL saveVerticalScrollerEnabled, saveHorizontalScrollerEnabled;
}
- (void)saveState;
- (void)setEnabled:(BOOL)bEnable;
@end
----- ExtendedTableView.m -----
#import <AppKit/AppKit.h>
#import "ExtendedTableView.h"
@implementation ExtendedTableView
- (BOOL)acceptsFirstResponder
{
return [self isEnabled];
}
- (void)dealloc
{
[saveBackgroundColors release];
[saveTextColors release];
[super dealloc];
}
- (void)saveState
{
NSEnumerator *e = [[self tableColumns] objectEnumerator];
NSTableColumn *curColumn;
if(saveBackgroundColors == nil)
saveBackgroundColors = [[NSMutableArray alloc] init];
if(saveTextColors == nil)
saveTextColors = [[NSMutableArray alloc] init];
[saveTextColors removeAllObjects];
[saveBackgroundColors removeAllObjects];
while ((curColumn = [e nextObject])) {
if([[curColumn dataCell] isKindOfClass:[NSTextFieldCell class]]) {
[saveTextColors addObject:[[curColumn dataCell] textColor]];
[saveBackgroundColors
addObject:[[curColumn dataCell] backgroundColor]];
}
}
saveVerticalScrollerEnabled = FALSE;
saveHorizontalScrollerEnabled = FALSE;
if([[self enclosingScrollView] hasVerticalScroller])
saveVerticalScrollerEnabled =
[[[self enclosingScrollView] verticalScroller] isEnabled];
if([[self enclosingScrollView] hasHorizontalScroller])
saveHorizontalScrollerEnabled =
[[[self enclosingScrollView] horizontalScroller] isEnabled];
}
- (void)setEnabled:(BOOL)bEnable
{
NSEnumerator *ec;
NSEnumerator *etc = nil, *ebc = nil;
NSTableColumn *curColumn;
NSColor *textColor = nil, *backgroundColor = nil;
if([self isEnabled] == bEnable) return;
[super setEnabled:bEnable];
ec = [[self tableColumns] objectEnumerator];
if(!bEnable) {
[self saveState];
textColor = [NSColor colorWithCalibratedWhite:0.50 alpha:1.0];
backgroundColor = [NSColor colorWithCalibratedWhite:0.94 alpha:1.0];
}
else {
etc = [saveTextColors objectEnumerator];
ebc = [saveBackgroundColors objectEnumerator];
}
while ((curColumn = [ec nextObject])) {
if([[curColumn dataCell] isKindOfClass:[NSTextFieldCell class]]) {
if(bEnable && etc && ebc) {
textColor = [etc nextObject];
backgroundColor = [ebc nextObject];
}
if(textColor)
[[curColumn dataCell] setTextColor:textColor];
if(backgroundColor)
[[curColumn dataCell] setBackgroundColor:backgroundColor];
}
}
if([[self enclosingScrollView] hasVerticalScroller])
[[[self enclosingScrollView] verticalScroller]
setEnabled:(bEnable && saveVerticalScrollerEnabled)];
if([[self enclosingScrollView] hasHorizontalScroller])
[[[self enclosingScrollView] horizontalScroller]
setEnabled:(bEnable && saveHorizontalScrollerEnabled)];
[self setNeedsDisplay:YES];
}
@end
----- End Files -----
B. Change your IBOutlet(s) from NSTextView to ExtendedTextView for all of
the text views to which you want to add Enabling capability.
C. Open the NIB file for your window. Drag a copy of ExtendedTextView.h
from your project into the Interface Builder window (to let IB know about
the new sub-class).
D. In Interface Builder, change the class for your NSTextView(s) to
ExtendedTextView. If the "CustomClass" of the control only says
"NSScrollView", then double-click on the TableView. You should then be
able to set the class to ExtendedTextView.
E. Make your window controller (or some other object) the delegate for
your table view(s). Then implement these delegated functions:
- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView
{  return [aTableView isEnabled];  }
- (BOOL)tableView:(NSTableView *)aTableView
shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{  return [aTableView isEnabled];  }
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex
{  return [aTableView isEnabled];  }
- (BOOL)tableView:(NSTableView *)aTableView
shouldSelectTableColumn:(NSTableColumn *)aTableColumn
{  return [aTableView isEnabled];  }
// (don't forget to add the declarations for these functions to your
//  .h include file)
And now you should be able to call [myExtendedTableView setEnabled:NO] and
have the view actually be disabled.
Note 1:  The code above only works for tables that made up of
NSTextFieldCell's. If you have other types of cells in your table, you
will need to add them to the code above.
Note 2:  Do not add or remove columns to the table when it is disabled, or
otherwise the colors will probably get screwed up when it is re-enabled.
30. How do I put a folder of files into my application?
-------------------------------------------------------
By now you have probably figured out that applications in Mac OS X are
actually folders full of information such as the executable, NIB files,
etc. This is a totally cool concept because it also allows you to hide
your own stuff (database files, help files, etc.) inside an application.
To include a folder with files in your application, drag the folder into
the project. A sheet will pop-down. Check "Create Folder References for
any added folders". The folders (and the files within them) will now be
copied to your application when you build your project. Note that the
folder is blue (instead of tan/yellow).
31. And then how do I get access to them?
-----------------------------------------
That's easy too, do this:
NSArray *myArray = [[NSBundle mainBundle] pathsForResourcesOfType:nil
inDirectory:@"MyFolder"];
to get a list of all the files (an array of NSString(s) which are full
path names) in your folder, or this:
NSString *myFilePath =
[[NSBundle mainBundle] pathForResource:@"MyFile" ofType:@"ext"
inDirectory:@"MyFolder"];
to get the full path name of a single file.
Once you have the full path name, you can open the files with ordinary
File I/O routines.
32. What is an informal protocol?
---------------------------------
Basically, it is a protocol that you don't have to define with
<TheProtocol> in your Objective-C class @interface specification.
The advantages are that it's easy to use. Just declare the methods you
need from the informal protocol in your class and then implement them.
The disadvantage is that others can't tell whether your class has
implemented the protocol or not.
Click here for more information on informal protocols.
33. What is the NSURLClient informal protocol? How do I download a URL in
the background?
-------------------------------------------------------------------------
Here is the NSURLClient informal protocol:  (from the Mac OS X Developer
Release Notes)
@interface NSObject(NSURLClient)
- (void)URL:(NSURL *)sender
resourceDataDidBecomeAvailable:(NSData *)newBytes;
- (void)URLResourceDidFinishLoading:(NSURL *)sender;
- (void)URLResourceDidCancelLoading:(NSURL *)sender;
- (void)URL:(NSURL *)sender
resourceDidFailLoadingWithReason:(NSString *)reason;
@end
The purpose of the class is to allow you to easily download URLs in the
background. Just implement the methods (in any object you like) that you
want.
The steps to download a URL in the background are:
A. Create an NSURL object with the URL you want to load.
B. Execute the [myURL loadResourceDataNotifyingClient:usingCache:] method.
Provide some object of yours as the "client" to this call.
C. In your client object, implement whichever classes from the NSURLClient
informal protocol that you would like. In particular, to get the contents
of the file you will likely need to implement this method:
- (void)URLResourceDidFinishLoading:(NSURL *)sender
{
NSData *dataContents = [sender resourceDataUsingCache:YES];
[dataContents writeToFile:@"myfile.html" atomically:YES];
// (or do whatever you want to do with the URL data)
}
34. How do I cancel an [NSURL loadResourceDataNotifyingClient:] in
progress?
------------------------------------------------------------------
You can't. Instead, you need to use NSURLHandle. But don't worry: it's
easy, just follow these steps:
A. Decide which of your classes will receive notifications from
NSURLHandle and add the <NSURLHandleClient> implements-protocol specifier
to it's @interface line, for example:
@interface MyInternetController : NSWindowController<NSURLHandleClient>
{  . . . }
@end
B. Implement whichever of these routines in your class you want:
- (void)URLHandleResourceDidBeginLoading:(NSURLHandle *)sender;
- (void)URLHandleResourceDidCancelLoading:(NSURLHandle *)sender;
- (void)URLHandleResourceDidFinishLoading:(NSURLHandle *)sender;
- (void)URLHandle:(NSURLHandle *)sender
resourceDataDidBecomeAvailable:(NSData *)newBytes;
- (void)URLHandle:(NSURLHandle *)sender
resourceDidFailLoadingWithReason:(NSString *)reason;
Watch out:  Sometimes if there is a fast error, it appears that
URLHandleResourceDidBeginLoading can actually occur *after*
resourceDidFailLoadingWithReason.
C. Inside the URLHandleResourceDidFinishLoading: method, you can access
the data downloaded with:
NSData *myData = [sender resourceData];
(see #33 above).
E. To start the download, put this code into your class and call it:
- (void)download
{
nsURL = [[NSURL URLWithString:urlString] retain];
urlHandle = [[nsURL URLHandleUsingCache:NO] retain];
[urlHandle addClient:self];
[urlHandle loadInBackground];
}
// Don't forget to declare "nsURL" and "urlHandle" in your class and
// also to "release" them somewhere
F. If you need to cancel the download, then do this:
[urlHandle cancelLoadInBackground];
35. How do I launch a web browser to a URL?
-------------------------------------------
Use NSWorkspace:
BOOL isOK = [[NSWorkspace sharedWorkspace]
openURL:[NSURL URLWithString:@"http:// ... "]];
36. How do I add on-line help to my application?
------------------------------------------------
Additional documentation for on-line help can be found in:
"/Developer/Documentation/Apple Help/"
Basically, the steps are as follows:
A. Create your help as a folder of HTML files and image files (as
necessary). Your HTML files can have frames, tables... actually most
anything (there are also Apple extensions for launching programs and
running AppleScript).
B. Drag your folder into your ProjectBuilder window, probably under
"Resources" is best. A sheet will slide-down. Check "Create Folder
References for any added folders". The folders (and the files within them)
will now be copied to your application when you build your project. Note
that the folder is blue (instead of tan/yellow).
C. Choose one of your HTML pages to be the primary entry point. In order
for the Help system to locate this page, put a <META NAME="AppleTitle"...>
tag inside this file, like this:
<HEAD>
<META NAME="AppleTitle" CONTENT="MyApplication Name">
<TITLE>My Program Title</TITLE>
</HEAD>
D. Under the "Project" pull-down menu, choose "Edit Active Target".
E. Click on the "Application Settings" tab, and then on "Expert".
F. Add these two new properties to your target:
Property                 Class    Value
----------------------   ------   -----
CFBundleHelpBookName     String   MyApplication Name
CFBundleHelpBookFolder   String   MyApplication Folder
CFBundleHelpBookName should be the exact same string you specified in the
<META> tag.
CFBundleHelpBookFolder should be the exact same string you specified for
the folder which contains all of your HTML files.
G. In your MainMenu.nib (Interface Builder), edit the menu so that under
"Help" it says "MyApplication Help". Also, this menu command should be
connected to the "showHelp:" command of the FirstResponder obejct (turns
out this is ultimately handled by NSApplication).
Troubleshooting:
*  If you get an error message which says "Help isn't available for
MyApplication", then probably the name of your folder doesn't match the
CFBundleHelpBookFolder, or your folder was improperly included into your
project (it's yellow, not blue), or it's in the wrong place, or something
like that.
*  If, when you launch help, you go directly to the "Help Center" instead
to your main application, then you will probably see your application's
help in the list somewhere. This could occur for several reasons:  1) the
name for CFBundleHelpBookName does not match the <META> tag in your
primary help file, 2) you have multiple HTML files in your folder and more
than one of them specify a <META NAME="AppleTitle"...> tag, or 3) there
are HTML syntax errors in your file (i.e. missing <HTML> tags, missing
angle-brackets, etc).
37. How do I jump to a specific page of my on-line help?
--------------------------------------------------------
You need to use functions available in the Carbon APIs to do this,
specifically "AHGotoPage()". Fortunately, including them in your project
is pretty easy. Follow these steps:
A. Put the following files in your project:
----- HelpPage.h -----
#import <AppKit/AppKit.h>
#import <Carbon/Carbon.h>
void OpenHelpPage(NSString *page);
----- HelpPage.m -----
#include "HelpPage.h"
void
OpenHelpPage(NSString *page)
{
NSString *helpBookName =
[[[ NSBundle mainBundle ] infoDictionary ]
objectForKey:@"CFBundleHelpBookName" ] ;
AHGotoPage( (CFStringRef)helpBookName, (CFStringRef)page, nil );
}
----- End -----
B. Add the Carbon framework to your project. Execute the pull-down menu
comment:  Project / Add Frameworks..., click on "Carbon.framework" and
then click "Open".
C. Wherever you need to use the new function, import the "HelpPage.h"
include file.
D. Then make calls as follows:
OpenHelpPage(@"mypage.html");
Will open the "mypage.html" file located in your Help folder.
38. Why can't I scan past \n with NSScanner?
--------------------------------------------
Because it is in the "characters to be skipped" list.
For example, the following code:
NSScanner *myScanner;
myScanner = [NSScanner scannerWithString:@"\nThis is a string"];
[myScanner scanCharactersFromSet:
[NSCharacterSet characterSetWithCharactersInString:@" \r\t\n"]
intoString:nil];
// > The scanner is now positioned on the "\n" character,
//   not the "T" of "This"
To fix this issue, add the following line to your code:
[myScanner setCharactersToBeSkipped:nil];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值