Home > Sample Code > Attributed Strings in iOS
Attributed Strings in iOS
Posted on Thursday, March 10, 2011 by android apps market for tablests
Ten months ago when the original iPad shipped, Apple released iOS 3.2, and for the first time, iOS developers had access to NSAttributedString and NSMutableAttributedString, objects designed to hold strings along with font, paragraph, and style information. We no longer had to resort to using heavy UIWebViews or complex Core Graphics calls to draw styled text.
Well, sort of�
On the Mac side of things, NSAttributedString and its counterpart NSMutableAttributedString have been around for a long, long time, as part of Foundation. But, there's also been, for nearly as long, categories on both of these classes in App Kit called the Application Kit Additions which have all sorts of useful additional methods.
These categories provide ways to create attributed strings from various sorts of formatted text documents (RTF, HTML), to create attributed strings by specifying multiple specified attributes, to tweak existing attributes, to draw the attributed string, and to determine the size of an attributed string if it were to be drawn.
In fact, most of the really useful methods for these two classes are contained in these App Kit categories and not in the base classes. Unfortunately, we don't have those categories in the iOS SDK, or even a scaled back version of them. We just have the base classes. That means we have a whopping thirteen methods on NSAttributedString, and another thirteen on NSMutableAttributedString.
Cocoa has luxury-brand attributed strings; Cocoa touch has store-brand generic ones.
Even weirder, NSAttributedString has an init method that takes a dictionary of string attributes, but the key constants for using that method aren't even included in iOS in either the public headers or the documentation. The description of the methods that take these attributes state that the constants are in the Overview section of the documentation, but that's actually only true in the Mac OS X documentation, not the iOS documentation.
In other words, you can't create an NSAttributedString or NSMutableAttributedString using initWithString:attributes: because you don't have the constants you need in order to specify the various attributes. That's not entirely true; you actually are able to use the Core Text counterparts of the NSAttributedString constants , such as kCTForegroundColorAttributeName in place of NSForegroundColorAttributeName, however this isn't actually documented anywhere, and there isn't an exact 1:1 correlation between the NS and CT string attributes (though it's close).
This situation is really odd. Apple went through great efforts to give us all the low-level pieces need to do complex text rendering, but didn't give us higher-level objects to handle most that functionality elegantly. We have the lion's share of all of the low-level Core Text and Core Graphics calls that are available on Mac OS X (still no Core Image, though). Yet, we have to write low-level Core Text and Core Graphics code to do the bulk of even the most common typesetting tasks using attributed strings.
Fortunately, NSAttributedString and NSMutableAttributedString are both toll-free bridged to their Core Foundation counterparts CFAttributedStringRef and CFMutableAttributedStringRef respectively. That means you can create, for example, a CFAttributedStringRef and simply cast it to an NSAttributedString pointer, and then calling NSAttributedString methods on it will work.
Mostly.
There's one gotcha here. On iOS, UIFont and CTFont are not toll-free bridged, even though NSFont and CTFont on the Mac are. You cannot pass a UIFont into a function that expects a CTFont and vice versa.
To get a CTFont from a UIFont, you can do this:
Notice the name of this method - the word "create" in the function name indicates that the returned CTFont object has been retained for you, and you are responsible for calling CFRelease() on it when you're done with it, to avoid leaking.
Going the other way, from a CTFont to a UIFont is only a little more involved. Here's a category method on UIFont that will create an instance of UIFont based on a CTFontRef pointer
Once you have the ability to convert the two font objects into each other, creating attributed strings really isn't that bad. Here's an example category method on NSMutableAttributedString that will create an instance by taking an NSString plus a font, a font size, and a constant representing the desired text justification. It will return an autoreleased attributed string with the text attributes applied to the entire string:
What about calculating the space needed to draw an attributable string? That's a little more involved, but it can be done. Here are two category methods on NSAttributedStringthat will tell you how much space an attributed string will require when drawn at a specified width or height, which is a useful thing to know when laying out text:
NB(1): This is a new version that's both shorter, and fixes a bug with the original version.
NB(2): A couple of people on Twitter have commented that you should save a reference to your CTFrameSetterRef when calculating height or width and re-use it, because the framesetter will cache those calculations. If you use a new one, you not only have the overhead of a new object, you will also be doing the size calculation twice. I'm planning a future post where I show how to draw attributed strings, and I need to give some thought about how to re-architect the code for that post based on that feedback.
I assume it's only a matter of time before Apple gives us the NSAttributedString UIKit Additions category, or some similar higher-level functionality. In the meantime, any time you have to deal with attributed strings, the best bet is to figure out how to do what you need to do in Core Text and/or Core Graphics (Apple's Programming Guides actually show exactly how to do the most common tasks using both of these frameworks), then wrap a generic version of that code into a category method on NSAttributedString or NSMutableAttributableString.
Well, sort of�
On the Mac side of things, NSAttributedString and its counterpart NSMutableAttributedString have been around for a long, long time, as part of Foundation. But, there's also been, for nearly as long, categories on both of these classes in App Kit called the Application Kit Additions which have all sorts of useful additional methods.
These categories provide ways to create attributed strings from various sorts of formatted text documents (RTF, HTML), to create attributed strings by specifying multiple specified attributes, to tweak existing attributes, to draw the attributed string, and to determine the size of an attributed string if it were to be drawn.
In fact, most of the really useful methods for these two classes are contained in these App Kit categories and not in the base classes. Unfortunately, we don't have those categories in the iOS SDK, or even a scaled back version of them. We just have the base classes. That means we have a whopping thirteen methods on NSAttributedString, and another thirteen on NSMutableAttributedString.
Cocoa has luxury-brand attributed strings; Cocoa touch has store-brand generic ones.
Even weirder, NSAttributedString has an init method that takes a dictionary of string attributes, but the key constants for using that method aren't even included in iOS in either the public headers or the documentation. The description of the methods that take these attributes state that the constants are in the Overview section of the documentation, but that's actually only true in the Mac OS X documentation, not the iOS documentation.
In other words, you can't create an NSAttributedString or NSMutableAttributedString using initWithString:attributes: because you don't have the constants you need in order to specify the various attributes. That's not entirely true; you actually are able to use the Core Text counterparts of the NSAttributedString constants , such as kCTForegroundColorAttributeName in place of NSForegroundColorAttributeName, however this isn't actually documented anywhere, and there isn't an exact 1:1 correlation between the NS and CT string attributes (though it's close).
This situation is really odd. Apple went through great efforts to give us all the low-level pieces need to do complex text rendering, but didn't give us higher-level objects to handle most that functionality elegantly. We have the lion's share of all of the low-level Core Text and Core Graphics calls that are available on Mac OS X (still no Core Image, though). Yet, we have to write low-level Core Text and Core Graphics code to do the bulk of even the most common typesetting tasks using attributed strings.
Fortunately, NSAttributedString and NSMutableAttributedString are both toll-free bridged to their Core Foundation counterparts CFAttributedStringRef and CFMutableAttributedStringRef respectively. That means you can create, for example, a CFAttributedStringRef and simply cast it to an NSAttributedString pointer, and then calling NSAttributedString methods on it will work.
Mostly.
There's one gotcha here. On iOS, UIFont and CTFont are not toll-free bridged, even though NSFont and CTFont on the Mac are. You cannot pass a UIFont into a function that expects a CTFont and vice versa.
To get a CTFont from a UIFont, you can do this:
CTFontRef CTFontCreateFromUIFont(UIFont *font)
{
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)font.fontName,
font.pointSize,
NULL);
return ctFont;
}Notice the name of this method - the word "create" in the function name indicates that the returned CTFont object has been retained for you, and you are responsible for calling CFRelease() on it when you're done with it, to avoid leaking.
Going the other way, from a CTFont to a UIFont is only a little more involved. Here's a category method on UIFont that will create an instance of UIFont based on a CTFontRef pointer
@implementation UIFont(MCUtilities)
+ (id)fontWithCTFont:(CTFontRef)ctFont
{
CFStringRef fontName = CTFontCopyFullName(ctFont);
CGFloat fontSize = CTFontGetSize(ctFont);
UIFont *ret = [UIFont fontWithName:(NSString *)fontName size:fontSize];
CFRelease(fontName);
return ret;
}
@endOnce you have the ability to convert the two font objects into each other, creating attributed strings really isn't that bad. Here's an example category method on NSMutableAttributedString that will create an instance by taking an NSString plus a font, a font size, and a constant representing the desired text justification. It will return an autoreleased attributed string with the text attributes applied to the entire string:
+ (id)mutableAttributedStringWithString:(NSString *)string font:(UIFont *)font color:(UIColor *)color alignment:(CTTextAlignment)alignment
{
CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
if (string != nil)
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef)string);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTForegroundColorAttributeName, color.CGColor);
CTFontRef theFont = CTFontCreateFromUIFont(font);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTFontAttributeName, theFont);
CFRelease(theFont);
CTParagraphStyleSetting settings[] = {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTParagraphStyleAttributeName, paragraphStyle);
CFRelease(paragraphStyle);
NSMutableAttributedString *ret = (NSMutableAttributedString *)attrString;
return [ret autorelease];
}What about calculating the space needed to draw an attributable string? That's a little more involved, but it can be done. Here are two category methods on NSAttributedStringthat will tell you how much space an attributed string will require when drawn at a specified width or height, which is a useful thing to know when laying out text:
NB(1): This is a new version that's both shorter, and fixes a bug with the original version.
NB(2): A couple of people on Twitter have commented that you should save a reference to your CTFrameSetterRef when calculating height or width and re-use it, because the framesetter will cache those calculations. If you use a new one, you not only have the overhead of a new object, you will also be doing the size calculation twice. I'm planning a future post where I show how to draw attributed strings, and I need to give some thought about how to re-architect the code for that post based on that feedback.
- (CGFloat)boundingWidthForHeight:(CGFloat)inHeight
{
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (CFMutableAttributedStringRef) self);
CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(CGFLOAT_MAX, inHeight), NULL);
CFRelease(framesetter);
return suggestedSize.width;
}
- (CGFloat)boundingHeightForWidth:(CGFloat)inWidth
{
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (CFMutableAttributedStringRef) self);
CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(inWidth, CGFLOAT_MAX), NULL);
CFRelease(framesetter);
return suggestedSize.height;
}I assume it's only a matter of time before Apple gives us the NSAttributedString UIKit Additions category, or some similar higher-level functionality. In the meantime, any time you have to deal with attributed strings, the best bet is to figure out how to do what you need to do in Core Text and/or Core Graphics (Apple's Programming Guides actually show exactly how to do the most common tasks using both of these frameworks), then wrap a generic version of that code into a category method on NSAttributedString or NSMutableAttributableString.
Category Article iOS SDK, iPhone SDK, Sample Code
Powered by Blogger.
Blog Archive
-
▼
2011
(4034)
-
▼
March
(1475)
- Acura to be official car of spy agency S.H.I.E.L.D...
- Opera Mini Back in GetJar's App Store
- Nexus S XL Axiata Offered with Package bonus
- Nanogenerators produce electricity by squeezing yo...
- Richard Hammond admits regret over Top Gear Mexico...
- Full profile Android Smartphone "HTC EVO 3D CDMA "...
- SIM-Drive's SIM-LEI electric car achieves 207-mile...
- Profile and price CDMA Android Phone Huawei Ideos ...
- The Colorful and Mysterious Lego Box
- Jailbreak and Unlock iPhone 3G/3GS Using Blacksn0w
- Ninja Saga Level 10 Weapon Cheat April 2011
- Zombie Driver MULTi7 - Free Download Game
- Free Download Assassin's Creed Brotherhood 2011 (B...
- CHERNOBYL TERRORIST ATTACK POSTMORTEM FREE DOWNLOA...
- Haunted Halls Green Hills Sanitarium v1.0 Cracked ...
- RFB Repack V3 Update Point Blank 01042011
- Cheat Point Blank Fnet ComeBack 01032011
- New Cheat 1 April 2011 AZV.1 1
- Cheat Point Blank New 1 April 2011 1 F2F Special W...
- Stay connected to the market, wherever you are
- MegaHockey v 1.2.1 IPHONE / IPOD TOUCH
- International Athletics v1.1 for IPHONE / IPOD TOUCH
- Bugs Wars Version 1.1.0 for IPHONE / IPOD TOUCH
- Shadow Era v1.24 **UPDDATED 31-MAR** for IPHONE / ...
- Google tightening control of Android, insisting li...
- HTC invites us to 'see what's next' on April 12th
- Samsung Galaxy S getting a 1.4GHz '2011 edition' n...
- NOKIA MAKES SYMBIAN SOURCE CODE AVAILABLE FOR THEI...
- INTRODUCING THE HTC FLYER TABLET
- ASUS Eee Pad Transformer shows up at Best Buy for ...
- NOKIA CREDIT RATING CUT BY S&P
- Haypi Kingdom - Advance Tips Swapping Crop Oasis &...
- SPB SHELL 3D FOR ANDROID
- March Reflections
- Sony Ericsson XPERIA X10 Mini Pro
- Acer Aspire 7736ZG-444G50Mn Laptop
- Nokia E73 Review | Specifications Nokia E73
- Samsung i5500 Corby a.k.a Galaxy 5 Android
- The New Magellan 2010 RoadMate GPS
- Garmin GPSMAP 62 review | GPSMAP 62 Series
- Pictures: iPhone vs HTC Touch HD
- iPhone Model MB717LL
- Nokia N8 Coming Soon to Vodafone UK
- LG C900 for AT&T has Windows Phone 7
- Motorola XT300 Mini Android with Portrait Slider
- ViewSonic Tablet to Dual-boot Windows 7 & Android 1.6
- The Top 50 Android Applications (46 public)
- Verizon BlackBerry Bold 9650 Review
- Epic Games: GEARS of WAR Anvil Gate
- Animated Themes Nokia 2690
- Download Neon Menu Theme for Nokia N95 8GB | Nokia...
- LG Encore Available at AT&T
- Astro File Manager 2.5.2 for Android
- Nokia E63 Themes, Nokia E71 Themes, Nokia S60v5 Th...
- SpongeBob Themes | Download Spongebob Themes
- Download Winnie The Pooh Themes for Nokia 5800
- Sony Ericsson W595 Themes
- New Nokia C3 Touch and Type Review
- Samsung Star II (S5260) Will Soon be Released in I...
- Samsung C414 Review, Specs, and Price
- Harga Axioo Neon HNM dan spesifikasinya
- ony Ericsson Xperia Arc Specifications and Features
- The Indonesia Government To Close Blackberry RIM F...
- Apple iPhone 4 CDMA Verizon Wireless Launched in USA
- Nokia E6-00 Prediction Specs, To Reveal at Mobile ...
- Axioo Pricelist of PICOPAD, NEON and ZETTA at Komp...
- Samsung Galaxy Mini S5570 And Samsung Galaxy Mini ...
- Fujitsu Lifebook Promo Price at Surabaya
- BlackBerry Torch 9800 Promo Price with HSBC Bank C...
- Pre-Order Nokia E7 Finland, Start Shipping On Feb...
- Nexian Batman G777 Gets Special Price with title N...
- Nexian Speed W701 New Year Special 777,000 Rupiah
- Nokia N9 MeeGo Specs To Reveal On February at MWC ...
- Hape Nexian Dynamic NX-G353 For Only 333,000 Rupiah
- Amosu Pink iPhone 4G, Pink iPad and Pink Blackberr...
- New Handphone Passed The Indonesian Telecommunicat...
- Samsung's Stick-Thin Bluetooth Headset
- Motorola Atrix 4G AT&T To Sell After Officially In...
- HP TouchPad With Camera 1.3MP for video calling
- Nexian Lolie NX-G730 Introduced With Anang - Aurel...
- Nexian Anang-Ashanty Xlalu Cinta, W701 Speed and G...
- AHA Touch (Huawei IDEOS C8150) CDMA Android Phone:...
- Nexian SuperStar G506 Dual On GSM With 2.0 inch QC...
- Tata Photon Follows MTS, Associates With Meru Cabs
- Economics of Telecom Business in India
- Vodafone and RIM Organizing Free BlackBerry Servic...
- Dell Latitude E6320 Business Laptop
- Augmented VR system lets you enjoy the weather you...
- Starcraft 2 : Starcraft Original Mode (2010/Eng/Mo...
- Dead Space 2 [Update 1] (2011/MULTi2/RePack by Spi...
- OUTBREAK - HEiST (PC/ENG) Free Download PC/PS Game
- Crysis 2 - Limited Edition (2011/PC/Multi9)-DL fre...
- Sniper Art Of Victory PC Game Free Download
- Star Wars Republic Commando 2010 (PC/ENG) Free Dow...
- Call Of Duty: American Rush 2 (PC/ENG) Free Full D...
- WinRAR 4.00 - Final FREE DOWNLOAD
- COMODO Internet Security 5.3.181415.1237 FREE DOWN...
- Free Download CCleaner 3.05.1408 Updated
- AVG Internet Security 2011 10.0.1209 Build 3533 fr...
- Daily U-Turn: What you missed on 3.30.11
-
▼
March
(1475)