iOS:NSAttributedString

來源:互聯網
上載者:User

http://iphonedevelopment.blogspot.com/2011/03/attributed-strings-in-ios.html

 

十個月以前,蘋果發布了iPad和iOS3.2。iOS開發人員終於可以使用NSAttributedString和NSMutableAttributedString了。它們(這兩個對象)可以將字串和相關字型、段落格式及格式化資訊儲存在一起。我們不需要使用“重量級”的UIWebView或複雜的CoreGraphics API來繪製文本。

在Mac上,NSAttributedString和NSMutableAttributedString出現在Foundation架構中已經有一些曆史了。此後不久,App Kit對這兩個類進行了類別化,增加了一系列方法——即所謂的Application Kit Additions。
這些類別可以從各種格式的文字文件(RTF、HTML)來建立格式化字串,通過指定各種標準屬性、擴充屬性繪製格式文本,還可以計算格式文本的繪製尺寸。

事實上,這兩個類真正有用的方法都在App Kit類別中,而不是在基類中。不幸的是,iOSSDK沒有這些類別,以及它們的簡化版本。我們只有這兩個基本的類。也就是說,我們只能使用NSAttributedString的13個方法和NSMutableAttributedString的13個方法。
Cocoa中有“豪華版”的“屬性化”字串;而Cocoa touch只有普通的字串。

更為古怪的是,NSattributedString有一個初始化方法,它有一個字串屬性字典的參數,但在iOS公開的標頭檔和文檔中找不到相關的key常量。在文檔的概述中,關於這個方法中使用到的屬性key常量的描述,只存在於MacOSX文檔,而不存在於iOS文檔。
換句話說,你不能用initWithString:attributes方法建立NSAttributedString或NSMutableAttributedString,因為你根本無法使用那些用於指定字串格式化屬性的常量。當然,你可以使用CoreText中相對應的常量,例如用Core Text中的 kCTForegroundColorAttributeName 來取代原本的NSForegroundColorAttributeName。但這不是官方文檔所推薦的,而且NS常量和CT常量並不是百分之百的能對應上(雖然很接近)。

有種情況很奇怪。蘋果致力於提供複雜文本繪製的底層介面,而不提供更進階別的類,以至於有許多功能我們沒有更雅緻的方式來處理。在 Mac OSX 獅子中,開放了所有的 CoreText 和
CoreGraphics 函數(但不包括 Core Image)。但是,哪怕是用 attributed string 實現一個最簡單的文本編輯程式,我們仍然得寫大量的底層CoreText 和 CoreGraphics 代碼。

幸運的是,NSAttributedString/NSMutableAttributedString和 CFAttributedStringRef/CFMutableAttributedStringRef 之間有免費橋接。也就是說,你建立了一個CFAttributedStringRef,可以將它簡單地轉換為一個 NSAttributedString 指標,然後調用NSAttributedString 對象支援的所有方法。
注意,在iOS 中,UIFont 和 CTFont 並不是免費橋接的關係,儘管在 Mac 它們下是。你不能將一個 UIFont 當成CTFont 傳遞給函數,反之亦然。

要想將 UIFont 轉換為 CTFont,你需要:

CTFontRefCTFontCreateFromUIFont(UIFont *font)
{
    CTFontRef ctFont =CTFontCreateWithName((CFStringRef)font.fontName,
                                           font.pointSize,
                                           NULL);
    return ctFont;
}

注意函數 CTFontCreateWithName 名中帶有 create 字樣,表明返回結果 CTFont 將是被 retain 過的,你應該負責調用CFRelease 去釋放它以免記憶體泄露。
將 CTFont 轉換為 UIFont 要麻煩點。下面有一個 UIFont 的類別方法,用於從 CTFontRef 指標建立一個UIFont 執行個體。

@implementationUIFont(MCUtilities)
+ (id)fontWithCTFont:(CTFontRef)ctFont
{
    CFStringRef fontName= CTFontCopyFullName(ctFont);
    CGFloat fontSize =CTFontGetSize(ctFont);
   
    UIFont *ret = [UIFontfontWithName:(NSString *)fontName size:fontSize];
    CFRelease(fontName);
    return ret;
}
@end

只要知道如何在 CTFont 和UIFont 之間進行轉換,建立 attributed string 其實是一件簡單的事情。下面是一個 NSMutableAttributedString 的類別方法,用指定的文字、字型、字型大小、以及文字屬性來建立NSMutableAttributedString。結果返回一個 autorelease 的 attributed string(將文字屬性應用到整個字串)。

+ (id)mutableAttributedStringWithString:(NSString*)string font:(UIFont *)font color:(UIColor *)coloralignment:(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};
    CTParagraphStyleRefparagraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) /sizeof(settings[0]));
   CFAttributedStringSetAttribute(attrString, CFRangeMake(0,CFAttributedStringGetLength(attrString)), kCTParagraphStyleAttributeName,paragraphStyle);   
   CFRelease(paragraphStyle);

   
   NSMutableAttributedString *ret = (NSMutableAttributedString*)attrString;
   
    return [retautorelease];
}

真煩,為什麼繪製 attributable  string 時需要計算所需的空間?但這是必須的。NSAttributedString 有兩個類別方法,用於告訴你假設指定繪製的寬度,attributed string 將佔據多高的空間。或者指定繪製的高度,將佔據多寬的空間。這在對文本進行布局時相當有用:
NB(1): 這是精簡後的代碼,同時修正了原來的一個錯誤。
NB(2): Twiter 上曾有人建議在CTFrameSetterRef 中儲存高或者寬的計算結果。由於 framesetter 緩衝了計算結果,因此我們可以重用它。如果使用新的framesetter,則不要僅多建立一個對象,而且需要計算兩次 size。我準備在討論如何繪製 attributed string 時再根據這些建議重構代碼。

-(CGFloat)boundingWidthForHeight:(CGFloat)inHeight
{
    CTFramesetterRefframesetter = CTFramesetterCreateWithAttributedString((CFMutableAttributedStringRef) self);
    CGSize suggestedSize= CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0),NULL, CGSizeMake(CGFLOAT_MAX, inHeight), NULL);
   CFRelease(framesetter);
    returnsuggestedSize.width;  
}
- (CGFloat)boundingHeightForWidth:(CGFloat)inWidth
{
    CTFramesetterRefframesetter = CTFramesetterCreateWithAttributedString((CFMutableAttributedStringRef) self);
    CGSize suggestedSize= CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0),NULL, CGSizeMake(inWidth, CGFLOAT_MAX), NULL);
   CFRelease(framesetter);
    returnsuggestedSize.height;
}

我估計蘋果遲早會提供NSAttributedString UIKit Additions 類別或其他類似的進階 API(譯者註:實際上前者已經在iOS6 中提供了)。同時,你必須和 attributed string 打交道,必要時可能要用到 CoreText 或 CoreGraphics(蘋果的編程指南中對於如何使用這兩個架構完成大部分常見任務進行了詳細介紹),當然你也可以將這些代碼封裝為NSAttributedString 或 NSMutableAttributedString 的類別方法。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.