首发于公众号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。