Calculating text size with NSLayoutManager
I'm working on a view that uses the TextKit framework to typeset text in columns like this:
I am using UIView
cross-cut borders (black rectangle) to calculate 10 CGRects
, which I then convert to NSTextContainers
(red rectangles). In drawRect:
I pass them in NSLayoutManager
, which type and draw glyphs for me.
My question is: How can I calculate the number of columns required? I can draw a constant number of columns, but with different text lengths, I need to adjust the number of columns programmatically.
I found a method [NSLayoutManager glyphRangeForTextContainer:]
that returns a range of length 0 when the text container is left empty. So I could create loops to create text containers and use that method to determine if more containers are needed. However, this method is considered ineffective as it triggers a layout computation and I am not happy to run it in a loop, perhaps hundreds of times.
There must be a better way!
Thanks for your answers, Pete.
source to share
Well, after some searching around within TextKit, I finally found the answer.
My code runs in a loop like this:
while ([self needsMoreColumns]) {
[self addColumn];
}
...
- (BOOL)needsMoreColumns {
// Always create at least one column
if (self.layoutManager.textContainers.count == 0)
return YES;
// Find out the glyph range of the last column
NSRange range = [self.layoutManager glyphRangeForTextContainer:[self.layoutManager.textContainers lastObject]];
NSUInteger glyphs = [self.layoutManager numberOfGlyphs];
// Compare it with the number of glyphs
return range.location + range.length < glyphs;
}
I have not included the method [self addColumn]
as this is not a problem. It just uses the geometry of my layout and the position of the last column (if any) to calculate the CGRect
next one. It then creates NSTextContainer
with the appropriate size and stores the origin
rectangle property in a dedicated array for drawing purposes.
I also discovered methods [NSLayoutManager firstUnlaidCharacterIndex]
and [NSLayoutManager firstUnlaidGlyphIndex]
, but they don't seem to work as expected. After placing three columns of text in one column, they returned the length of the entire line, not the position of the first character, which did not fit in the first column. This is why I rather used a range based approach.
To keep all people safe! Pete.
source to share