CoreText初识
Befriending Core TextBefore the iPad was released you had basically two ways how to get text on screen. Either you would stick with UILabel or UITextView provided by UIKit or if you felt hard-core you would draw the text yourself on the Quartz level incurring all the headaches induced by having to mentally switch between Objective-C and C API functions.
As of iOS 3.2 we gained a third alternative in Core Text promising full control over styles, thread safety and performance. However for most of my apps I did not want to break 3.x compatibility and so I procrastinated looking at this powerful new API. Apps running only on iPads could have made use of Core Text from day 1, but to me it made more sense supporting iPad via hybrid apps where the iPhone part would still be backwards compatible.
Now as the year has turned the adoption of 4.x on all iOS platforms is ever more accelerating. Many new iPads where found under the Christmas tree and by now even the most stubborn people (read needing 3.x for jailbreaking and sim-unlocking) have little reason to stick with 3.x. Thus we have almost no incentive left to stick with 3.x compatibility. Yay!
Which brings us to Core Text. This API has been available on OSX for a while already, some of the advanced features like vertical text have been left out of the iPhone API.
Why do I need it?
Which methods do we know to draw text on screen? We have the low-level text drawing functions on the CoreGraphics level. We have UIKit classes UILabel and UITextView. But neither of these provide any easy way to draw mixed-style text. Often developers have to resort to using HTML in UIWebView just to draw something like bold words or a hyperlink amongst regular plain text.
This is the need that gets filled by CoreText. Almost.
The basic unit to describe strings with formatting are NSAttributedString and its mutable descendant NSMutibleAttributedString. As their names suggest they consist of NSStrings where different parts can have varying attributes. Now if you look at the documentation of NSAttributedString on OSX they have methods to create such strings straight from HTML. Boy that would be really handy. But until Apple chooses to port these methods over to iOS we are stuck with creating our own attributed strings.
Nevertheless even at this somewhat crippled stage CoreText is worth looking at to ease some of the mixed-style text drawing problems we might be facing.
Drawing “Simple” Lines – Not So Simple
At its 易做图st you draw individual lines with Core Text. For this you create a font, create an attributed text and then just draw the line. Even at its 易做图st there is quite a bit of typing involved. Of course we need to add the CoreText framework and header, in this case I am customizing a UIView subclass.
- (void)drawRect:(CGRect)rect
{
// create a font, quasi systemFontWithSize:24.0
CTFontRef sysUIFont = CTFontCreateUIFontForLanguage(kCTFontSystemFontType,
24.0, NULL);
// create a naked string
NSString *string = @"Some Text";
// blue
CGColorRef color = [UIColor blueColor].CGColor;
// single underline
NSNumber *underline = [NSNumber numberWithInt:kCTUnderlineStyleSingle];
// pack it into attributes dictionary
NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
(id)sysUIFont, (id)kCTFontAttributeName,
color, (id)kCTForegroundColorAttributeName,
underline, (id)kCTUnderlineStyleAttributeName, nil];
// make the attributed string
NSAttributedString *stringToDraw = [[NSAttributedString alloc] initWithString:string
attributes:attributesDict];
// now for the actual drawing
CGContextRef context = UIGraphicsGetCurrentContext();
// flip the coordinate system
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// draw
CTLineRef line = CTLineCreateWithAttributedString(
(CFAttributedStringRef)stringToDraw);
CGContextSetTextPosition(context, 10.0, 10.0);
CTLineDraw(line, context);
// clean up
CFRelease(line);
CFRelease(sysUIFont);
[stringToDraw release];
}
It’s particularly unnerving that Apple is mixing CF-style and Objective-C , but we have to live with that.
Another Font Class
Note that the core text font object used here is not the one we are used to using, UIFont. Thus we also don’t get the toll-free bridging to CGFontRef. This one here is optimized for Core Text. The example uses the method 易做图ogous to systemFontWithSize:, there are several more options to get a usable font.
// create it from the postscript name
CTFontRef helveticaBold = CTFontCreateWithName(CFSTR("Helvetica-Bold"), 24.0, NULL);
// create it by replacing traits of existing font, this replaces bold with italic
CTFontRef helveticaItalic = CTFontCreateCopyWithSymbolicTraits(helveticaBold, 24.0, NULL,
kCTFontItalicTrait, kCTFontBoldTrait | kCTFontItalicTrait);
Where a font name is used you have to use the postscript name. A sample list of all built-in fonts on iPhone/iPad refer to my article on Understanding UIFont. As of iOS 3.2 you can also add your own fonts by registering them in the info.plist and adding them to the app bundle.
To appreciate the level of customization available to use consider the different font traits available, on UIFont we had only normal, bold, italic and bold-italic.
Italic
Bold
Expanded
Condensed
MonoSpace
Vertical
Optimized for rendering UI Elements
If the above has not tied your brain in a knot yet, then thinking about the transformation matrices surely will. You are dealing with multiple coordinate systems which have their origin (0,0) in different locations. Quartz generally has it in the lower left, CoreText in the upper left. Add to this the fact that UIKit preflips the context you get to use in drawRect.
Underlining is not a feature of the font itself, but of NSAttributedString, likely because you can underline text in ANY font. These are your options:
None
Single Line
Double Line
Thick Line
These can be bitwise OR combined with several underlining line styles: