The next step is to get the string to appear in our view. At the end of the chapter, your application will look like Figure 17.1. The character being displayed will change as you type.
Overall, the class NSFont
has basically only two types of methods:
Class methods for getting the font you want
Methods for getting metrics on the font, such as letter height
+ (NSFont *)fontWithName:(NSString *)fontName size:(float)fontSize
This method returns a font object. fontName
is a family-face name, such as “HelveticaBoldOblique” or “Times-Roman.” If you use a fontSize
of 0.0, this method uses the default User Font size.
+ (NSFont *)userFixedPitchFontOfSize:(float)fontSize + (NSFont *)userFontOfSize:(float)fontSize + (NSFont *)messageFontOfSize:(float)fontSize + (NSFont *)toolTipsFontOfSize:(float)fontSize + (NSFont *)titleBarFontOfSize:(float)fontSize
These methods return the user's default font for the corresponding string types. Once again, a size of 0.0 will get a font of the default size.
Sometimes you want to display a string that has certain attributes for a range of characters. As an example, suppose you want to display the string “Big Nerd Ranch,” and you want the letters 0 through 2 to be underlined, the letters 0 through 7 to be green, and the letters 9 through 13 to be subscripts.
When dealing with a range of numbers, Cocoa uses the struct NSRange
. NSRange
has two members: location
and length
are both integers. The location
is the index of the first item, and the length
is the number of items in the range. You can use the function NSMakeRange()
to create an NSRange
.
To create strings with attributes that remain in effect over a range of characters, Cocoa has NSAttributedString
and NSMutableAttributedString
. Here is how you could create the NSAttributedString
just described:
NSMutableAttributedString *s; s = [[NSMutableAttributedString alloc] initWithString:@"Big Nerd Ranch"]; [s addAttribute:NSFontAttributeName value:[NSFont userFontOfSize:22] range:NSMakeRange(0, 14)]; [s addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:1] range:NSMakeRange(0,3)]; [s addAttribute:NSForegroundColorAttributeName value:[NSColor greenColor] range:NSMakeRange(0, 8)]; [s addAttribute:NSSuperscriptAttributeName value:[NSNumber numberWithInt:-1] range:NSMakeRange(9,5)];
Once you have an attributed string, you can do lots of stuff with it.
[s drawInRect:[self bounds]]; // Put it in a text field [textField setAttributedStringValue:s]; // Put it on a button [button setAttributedTitle:s];
Figure 17.2 shows the result of this code's execution.
Here are the names of the global variables for the most commonly used attributes and what they mean:
| A font object. By default, 12-point Helvetica. |
| A color. By default, black. |
| A color. By default, no background drawn. |
| A color. By default, the same as the foreground. |
| A number. By default, 0 (which means no underline). |
| A number. By default, 0 (which means no superscripting or subscripting). |
| An |
A list of all the attribute names can be found in <AppKit/NSAttributedString.h>
.
The easiest way to create attributed strings is from a file. NSAttributedString
can read and write the following file formats:
A string: You read a text file.
RTF: Rich Text Format is a standard for text with multiple fonts and colors. In this case, You will read and set the contents of the attributed string with an instance of NSData
.
RTFD: This is RTF with attachments. Besides the multiple fonts and colors of RTF, you can have images.
HTML: The attributed string can do basic HTML layout, but you probably want to use the WebView
for best quality. NSAttributedString
reads HTML, but does not write it.
Word: The attributed string can read and write simple .doc files.
When you read a document in, you may want to know some things about it, such as the paper size. If you supply a place where the method can put a pointer to a dictionary, the dictionary will have all the extra information that it could get from the data. For example:
NSDictionary *myDict; NSData *data = [NSData dataWithContentsOfFile:@"myfile.rtf"]; NSAttributedString *aString; aString = [[NSAttributedString alloc] initWithRTF:data documentAttributes:&myDict];
If you don't care about the document attributes, just supply NULL
.
Both NSString
and NSAttributedString
have methods that cause them to be drawn onto a view. NSAttributedString
has the following methods:
- (void)drawAtPoint:(NSPoint)aPoint
Draws the receiver. aPoint
is the lower-left corner of the string.
- (void)drawInRect:(NSRect)rect
Draws the receiver. All drawing occurs inside rect
. If rect
is too small for the string to fit, the drawing is clipped to fit inside rect
.
- (NSSize)size
Returns the size that the receiver would be if drawn.
NSString
has analogous methods. With NSString
, you need to supply a dictionary of attributes to be applied for the entire string.
- (void)drawAtPoint:(NSPoint)aPoint withAttributes:(NSDictionary *)attribs
Draws the receiver with the attributes in attribs
.
- (void)drawInRect:(NSRect)aRect withAttributes:(NSDictionary *)attribs
Draws the receiver with the attributes in attribs
.
- (NSSize)sizeWithAttributes:(NSDictionary *)attribs
Returns the size that the receiver would be if drawn with the atttibutes in attribs
.
Open BigLetterView.h
. Add an instance variable to hold the attributes dictionary. Declare the methods that you are about to implement:
#import <Cocoa/Cocoa.h> @interface BigLetterView : NSView { NSColor *bgColor; NSString *string; NSMutableDictionary *attributes; } - (void)prepareAttributes; - (void)drawStringCenteredIn:(NSRect)bounds; - (void)setBgColor:(NSColor *)c; - (void)setString:(NSString *)c; - (NSString *)string; @end
Open BigLetterView.m
. Create a method that creates the attributes
dictionary with a font and a foreground color:
- (void)prepareAttributes { attributes = [[NSMutableDictionary alloc] init]; [attributes setObject:[NSFont fontWithName:@"Helvetica" size:75] forKey:NSFontAttributeName]; [attributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; }
In the initWithFrame:
method, call the new method:
- (id)initWithFrame:(NSRect)rect
{
if (self = [super initWithFrame:rect]) {
NSLog(@"initializing view");
[self prepareAttributes];
[self setBgColor:[NSColor yellowColor]];
[self setString:@" "];
}
return self;
}
In the setString:
method, tell the view that it needs to redisplay itself:
- (void)setString:(NSString *)c
{
c = [c copy];
[string release];
string = c;
NSLog(@"The string: %@", string);
[self setNeedsDisplay:YES];
}
Create a method that will display the string in the middle of a rectangle:
- (void)drawStringCenteredIn:(NSRect)r { NSPoint stringOrigin; NSSize stringSize; stringSize = [string sizeWithAttributes:attributes]; stringOrigin.x = r.origin.x + (r.size.width - stringSize.width)/2; stringOrigin.y = r.origin.y + (r.size.height - stringSize.height)/2; [string drawAtPoint:stringOrigin withAttributes:attributes]; }
Call that method from inside your drawRect:
method:
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
[bgColor set];
[NSBezierPath fillRect:bounds];
[self drawStringCenteredIn:bounds];
if ([[self window] firstResponder] == self) {
[[NSColor keyboardFocusIndicatorColor] set];
[NSBezierPath setDefaultLineWidth:4.0];
[NSBezierPath strokeRect:bounds];
}
}
Make sure you release the attributes
dictionary in the dealloc
method:
- (void)dealloc
{
[string release];
[attributes release];
[bgColor release];
[super dealloc];
}
Build and run the application. Note that keyboard events go to your view unless they trigger a menu item. Try pressing Command-w. It should close the window (even if your view is the first responder for the key window).
All of the drawing commands can be converted into PDF by the AppKit framework. The PDF data can be sent to a printer or to a file. Note that the PDF will always look as good as possible on any device, because it is resolution independent.
You have already created a view that knows how to generate PDF data to describe how it is supposed to look. Getting the PDF data into a file is really quite easy. NSView
has the following method:
- (NSData *)dataWithPDFInsideRect:(NSRect)aRect
This method creates a data object and then calls drawRect:
. The drawing commands that would usually go to the screen instead go into the data object. Once you have this data object, you simply save it to a file.
Open BigLetterView.m
and add a method that will create a save panel as a sheet:
- (IBAction)savePDF:(id)sender { NSSavePanel *panel = [NSSavePanel savePanel]; [panel setRequiredFileType:@"pdf"]; [panel beginSheetForDirectory:nil file:nil modalForWindow:[self window] modalDelegate:self didEndSelector: @selector(didEnd:returnCode:contextInfo:) contextInfo:NULL]; }
When the user has chosen the filename, the method didEnd:returnCode: contextInfo:
will be called. Implement this method in BigLetterView.m
:
- (void)didEnd:(NSSavePanel *)sheet returnCode:(int)code contextInfo:(void *)contextInfo { NSRect r; NSData *data; if (code == NSOKButton) { r = [self bounds]; data = [self dataWithPDFInsideRect:r]; [data writeToFile:[sheet filename] atomically:YES]; } }
Also, declare these methods in the BigLetterView.h
file:
- (IBAction)savePDF:(id)sender; - (void)didEnd:(NSSavePanel *)sheet returnCode:(int)code contextInfo:(void *)contextInfo;
Open the nib file. Drag in BigLetterView.h
so that savePDF:
will appear as one of the actions. Select the Save As… item under the File menu. Relabel it Save PDF…. (You may delete all of the other menu items from the menu, if you wish.) Make the Save PDF… menu item trigger the BigLetterView
's savePDF:
method (Figure 17.3).
Save and build the application. You should be able to generate a PDF file and view it in Preview (Figure 17.4).
You will notice that multi-keystroke characters (like “é”) are not handled by your BigLetterView
. To make this possible, you would need to add several methods that the NSInputManager
uses. This topic is beyond the scope of this book (I just wanted to show you how to get keyboard events), but you can learn about it in Apple's discussion of NSInputManager
(/Developer/Documentation/ Cocoa/Conceptual/InputManager/index.html
).
Sometimes you will have a font that is good but would be perfect if it were bold or italicized or condensed. NSFontManager
can be used to make this sort of conversion. You can also use a font manager to change the size of the font.
For example, imagine you have a font and would like a similar font, but bold. Here is the code:
fontManager = [NSFontManager sharedFontManager]; boldFont = [fontManager convertFont:aFont toHaveTrait:NSBoldFontMask];
Give the letter a shadow. The NSShadow
class has the following methods:
- (id)init; - (void)setShadowOffset:(NSSize)offset; - (void)setShadowBlurRadius:(float)val; - (void)setShadowColor:(NSColor *)color;