首發於公衆號ui
iOS7 以後,蘋果推出了用於解決文本排版問題的 TextKit 三件套:spa
使用方法比較簡單:code
[textStorage addLayoutManager:layoutManager];
[layoutManager addTextContainer:textContainer];
複製代碼
而後在 view 的 bounds 發生變化的時候調整一下 textContainer 的 size 就能夠了。component
可是,這裏有一處坑,會致使詭異的問題,用下面的方法計算文本須要的 Rect 時,始終會獲得 CGRectZero:orm
[layoutManager usedRectForTextContainer:textContainer];
複製代碼
這彷佛是 TextKit 的一個 bug(或者是 feature?)。 寫代碼的時候,順應着思惟,通常都會這麼初始化 TextKit:cdn
- (void)initTextKit {
textStorage = [[NSTextStorage alloc] init];
layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];
textContainer = [[NSTextContainer alloc] initWithSize:constrainedSize];
[layoutManager addTextContainer:textContainer];
}
複製代碼
嗯,根據它們的添加順序一路寫下來,看着很順暢,這有啥問題? 這就是看不見的坑,textStorage 初始化的時機太早了,順序應該放到最後,調整後的代碼以下:blog
- (void)initTextKit {
layoutManager = [[NSLayoutManager alloc] init];
textContainer = [[NSTextContainer alloc] initWithSize:constrainedSize];
[layoutManager addTextContainer:textContainer];
textStorage = [[NSTextStorage alloc] init];
[textStorage addLayoutManager:layoutManager];
}
複製代碼
addLayoutManager 的調用順序放在最後,完美解決 usedRectForTextContainer 沒法計算的問題。字符串
BTWget
Facebook 開源庫 ComponentKit 一樣有這個問題,在 CKTextKitContext.mm 裏:it
- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString
lineBreakMode:(NSLineBreakMode)lineBreakMode
maximumNumberOfLines:(NSUInteger)maximumNumberOfLines
constrainedSize:(CGSize)constrainedSize
layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory
{
if (self = [super init]) {
// Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock.
static std::mutex *__static_mutex = new std::mutex;
std::lock_guard<std::mutex> l(*__static_mutex);
// Create the TextKit component stack with our default configuration.
_textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]);
_layoutManager = layoutManagerFactory ? layoutManagerFactory() : [[NSLayoutManager alloc] init];
_layoutManager.usesFontLeading = NO;
[_textStorage addLayoutManager:_layoutManager];
_textContainer = [[NSTextContainer alloc] initWithSize:constrainedSize];
// We want the text laid out up to the very edges of the container.
_textContainer.lineFragmentPadding = 0;
_textContainer.lineBreakMode = lineBreakMode;
_textContainer.maximumNumberOfLines = maximumNumberOfLines;
[_layoutManager addTextContainer:_textContainer];
}
return self;
}
複製代碼
不信你能夠試一下:
[context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
CGRect usedRect = [layoutManager usedRectForTextContainer:textContainer];
}];
複製代碼
無論字符串的內容是什麼, usedRect 的值都是 CGRectZero。