之前用Text Kit寫Reader的時候,在分頁時要計算一段文本的尺寸大小,之前使用了NSString類的sizeWithFont:constrainedToSize:lineBreakMode:方法,但是該方法已經被iOS7 Deprecated了,而iOS7新出了一個boudingRectWithSize:options:attributes:context方法來代替:
很礙眼的黃色警告標志。
先來看看iOS7 SDK包中關於
boudingRectWithSize:options:attributes:context
方法的定義:
-
// NOTE: All of the following methods will default to drawing on a baseline, limiting drawing to a single line. // To correctly draw and size multi-line text, pass NSStringDrawingUsesLineFragmentOrigin in the options parameter. @interface NSString (NSExtendedStringDrawing) - (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0); - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0); @end
關於該方法,NSAttributedString其實也有一個同名的方法:
-
@interface NSAttributedString (NSExtendedStringDrawing) - (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0); - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0); @end
該方法在iOS6就可以使用了。
關於該類,有一篇關於NSAttributedString UIKit Additions Reference翻譯的文章:http://blog.csdn.net/kmyhy/article/details/8895643
里面就說到了該方法:
boundingRectWithSize:options:context:
返回文本繪制所占據的矩形空間。
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context
參數
size
寬高限制,用於計算文本繪制時占據的矩形塊。
The width and height constraints to apply when computing the string’s bounding rectangle.
options
文本繪制時的附加選項。可能取值請參考“NSStringDrawingOptions”。
context
context上下文。包括一些信息,例如如何調整字間距以及縮放。最終,該對象包含的信息將用於文本繪制。該參數可為 nil 。
返回值
一個矩形,大小等於文本繪制完將占據的寬和高。
討論
可以使用該方法計算文本繪制所需的空間。size 參數是一個constraint ,用於在繪制文本時作為參考。但是,如果繪制完整個文本需要更大的空間,則返回的矩形大小可能比 size 更大。一般,繪制時會采用constraint 提供的寬度,但高度則會根據需要而定。
特殊情況
為了計算文本塊的大小,該方法采用默認基線。
如果 NSStringDrawingUsesLineFragmentOrigin未指定,矩形的高度將被忽略,同時使用單線繪制。(由於一個 bug,在 iOS6 中,寬度會被忽略)
兼容性
- iOS 6.0 以后支持。
聲明於
NSStringDrawing.
另外,關於參數(NSStringDrawingOptions)options
-
typedef NS_ENUM(NSInteger, NSStringDrawingOptions) { NSStringDrawingTruncatesLastVisibleLine = 1 << 5, // Truncates and adds the ellipsis character to the last visible line if the text doesn't fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set. NSStringDrawingUsesLineFragmentOrigin = 1 << 0, // The specified origin is the line fragment origin, not the base line origin NSStringDrawingUsesFontLeading = 1 << 1, // Uses the font leading for calculating line heights NSStringDrawingUsesDeviceMetrics = 1 << 3, // Uses image glyph bounds instead of typographic bounds } NS_ENUM_AVAILABLE_IOS(6_0);
NSStringDrawingTruncatesLastVisibleLine:
如果文本內容超出指定的矩形限制,文本將被截去並在最后一個字符后加上省略號。如果沒有指定NSStringDrawingUsesLineFragmentOrigin選項,則該選項被忽略。
NSStringDrawingUsesLineFragmentOrigin:
繪制文本時使用 line fragement origin 而不是 baseline origin。
The origin specified when drawing the string is the line fragment origin and not the baseline origin.
NSStringDrawingUsesFontLeading:
計算行高時使用行距。(譯者注:字體大小+行間距=行距)
NSStringDrawingUsesDeviceMetrics:
計算布局時使用圖元字形(而不是印刷字體)。
Use the image glyph bounds (instead of the typographic bounds) when computing layout.
簡單寫了一個Demo來看看該方法的使用,並比較了一下各個options的不同,首先是代碼:
-
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:textView.text]; textView.attributedText = attrStr; NSRange range = NSMakeRange(0, attrStr.length); NSDictionary *dic = [attrStr attributesAtIndex:0 effectiveRange:&range]; // 獲取該段attributedString的屬性字典 // 計算文本的大小 CGSize textSize = [textView.text boundingRectWithSize:textView.bounds.size // 用於計算文本繪制時占據的矩形塊 options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading // 文本繪制時的附加選項 attributes:dic // 文字的屬性 context:nil].size; // context上下文。包括一些信息,例如如何調整字間距以及縮放。該對象包含的信息將用於文本繪制。該參數可為nil NSLog(@"w = %f", textSize.width); NSLog(@"h = %f", textSize.height);
再看看不同的options下控制台的輸出結果:
-
NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading 2013-09-02 21:04:47.470 BoudingRect_i7_Demo[3532:a0b] w = 322.171875 2013-09-02 21:04:47.471 BoudingRect_i7_Demo[3532:a0b] h = 138.000015 NSStringDrawingUsesLineFragmentOrigin // The specified origin is the line fragment origin, not the base line origin 2013-09-02 17:35:40.547 BoudingRect_i7_Demo[1871:a0b] w = 318.398438 2013-09-02 17:35:40.549 BoudingRect_i7_Demo[1871:a0b] h = 69.000000 NSStringDrawingTruncatesLastVisibleLine // Truncates and adds the ellipsis character to the last visible line if the text doesn't fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set. 2013-09-02 17:37:38.398 BoudingRect_i7_Demo[1902:a0b] w = 1523.408203 2013-09-02 17:37:38.400 BoudingRect_i7_Demo[1902:a0b] h = 13.800000 NSStringDrawingUsesFontLeading // Uses the font leading for calculating line heights 2013-09-02 17:40:45.903 BoudingRect_i7_Demo[1932:a0b] w = 1523.408203 2013-09-02 17:40:45.905 BoudingRect_i7_Demo[1932:a0b] h = 13.800000 NSStringDrawingUsesDeviceMetrics // Uses image glyph bounds instead of typographic bounds 2013-09-02 17:42:03.283 BoudingRect_i7_Demo[1956:a0b] w = 1523.408203 2013-09-02 17:42:03.284 BoudingRect_i7_Demo[1956:a0b] h = 13.800000
其中如果options參數為NSStringDrawingUsesLineFragmentOrigin,那么整個文本將以每行組成的矩形為單位計算整個文本的尺寸。(在這里有點奇怪,因為字體高度大概是13.8,textView中大概有10行文字,此時用該選項計算出來的只有5行,即高度為69,而同時使用NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin卻可以得出文字剛好有10行,即高度為138,這里要等iOS7官方的文檔出來再看看選項的說明,因為畢竟以上文檔是iOS6的東西)
如果為NSStringDrawingTruncatesLastVisibleLine或者NSStringDrawingUsesDeviceMetric,那么計算文本尺寸時將以每個字或字形為單位來計算。
如果為NSStringDrawingUsesFontLeading則以字體間的行距(leading,行距:從一行文字的底部到另一行文字底部的間距。)來計算。
各個參數是可以組合使用的,如NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine。
根據該方法我調整了一下Reader的分頁方法:(主要是將被iOS7 Deprecated的sizeWithFont:constrainedToSize:lineBreakMode:方法改成了boudingRectWithSize:options:attributes:context:方法來計算文本尺寸)
-
/* 判斷是否需要分頁和進行分頁動作 */ -(BOOL)paging { /* 獲取Settings中設定好的字體(主要是獲取字體大小) */ static const CGFloat textScaleFactor = 1.; // 設置文字比例 NSString *textStyle = [curPageView.textView tkd_textStyle]; // 設置文字樣式 preferredFont_ = [UIFont tkd_preferredFontWithTextStyle:textStyle scale:textScaleFactor]; //設置prferredFont(包括樣式和大小) NSLog(@"paging: %@", preferredFont_.fontDescriptor.fontAttributes); // 在控制台中輸出字體的屬性字典 /* 設定每頁的頁面尺寸 */ NSUInteger height = (int)self.view.bounds.size.height - 40.0; // 頁面的高度 /* 獲取文本的總尺寸 */ NSDictionary *dic = preferredFont_.fontDescriptor.fontAttributes; CGSize totalTextSize = [bookItem.content.string boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size; NSLog(@"w = %f", totalTextSize.width); NSLog(@"h = %f", totalTextSize.height); /* 開始分頁 */ if (totalTextSize.height < height) { /* 如果一頁就能顯示完,直接顯示所有文本 */ totalPages_ = 1; // 設定總頁數為1 charsPerPage_ = [bookItem.content length]; // 設定每頁的字符數 textLength_ = [bookItem.content length]; // 設定文本總長度 return NO; // 不用分頁 } else { /* 計算理想狀態下的頁面數量和每頁所顯示的字符數量,用來作為參考值用 */ textLength_ = [bookItem.content length]; // 文本的總長度 NSUInteger referTotalPages = (int)totalTextSize.height / (int)height + 1; // 理想狀態下的總頁數 NSUInteger referCharactersPerPage = textLength_ / referTotalPages; // 理想狀態下每頁的字符數 // 輸出理想狀態下的參數信息 NSLog(@"textLength = %d", textLength_); NSLog(@"referTotalPages = %d", referTotalPages); NSLog(@"referCharactersPerPage = %d", referCharactersPerPage); /* 根據referCharactersPerPage和text view的高度開始動態調整每頁的字符數 */ // 如果referCharactersPerPage過大,則直接調整至下限值,減少調整的時間 if (referCharactersPerPage > 1000) { referCharactersPerPage = 1000; } // 獲取理想狀態下的每頁文本的范圍和pageText及其尺寸 NSRange range = NSMakeRange(referCharactersPerPage, referCharactersPerPage); // 一般第一頁字符數較少,所以取第二頁的文本范圍作為調整的參考標准 NSString *pageText = [bookItem.content.string substringWithRange:range]; // 獲取該范圍內的文本 NSLog(@"%@", pageText); NSRange ptrange = NSMakeRange(0, pageText.length); NSDictionary *ptdic = [[bookItem.content attributedSubstringFromRange:ptrange] attributesAtIndex:0 effectiveRange:&ptrange]; CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:ptdic context:nil].size; // 若pageText超出text view的顯示范圍,則調整referCharactersPerPage NSLog(@"height = %d", height); while (pageTextSize.height > height) { NSLog(@"pageTextSize.height = %f", pageTextSize.height); referCharactersPerPage -= 2; // 每頁字符數減2 range = NSMakeRange(0, referCharactersPerPage); // 重置每頁字符的范圍 ptdic = [[bookItem.content attributedSubstringFromRange:range] attributesAtIndex:0 effectiveRange:&range]; CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:ptdic context:nil].size; pageText = [bookItem.content.string substringWithRange:range]; // 重置pageText pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:ptdic context:nil].size; // 獲取pageText的尺寸 } // 根據調整后的referCharactersPerPage設定好charsPerPage_ charsPerPage_ = referCharactersPerPage;