I'm working on a view which uses TextKit framework to typeset text in columns like this:
I use my UIView
's bounds with edge insets (black rectangle) to compute 10 CGRects
which I then transform into NSTextContainers
(red rectangles). In drawRect:
I pass those to the NSLayoutManager
which typesets and draws the glyphs for me.
My question is: How can I compute the number of columns required? I can draw a constant number of columns but with varying text lengths, I need to adjust the number of columns programmatically.
I found the method [NSLayoutManager glyphRangeForTextContainer:]
which returns range of length 0 when the text container is to be left empty. Therefore, I could loop to create text containers and use this method to determine if more containers are needed. However, this method is said to be inefficient as it triggers the layout computation and I'm not happy running it in a loop perhaps hundreds of times over.
There has to be a better way!
Thanks for your answers, Pete.
Well, after some digging through the TextKit framework I've finally found the answer.
My code works 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 didn't include the method [self addColumn]
as it's a no brainer. It simply uses the geometry of my layout and position of the last column (if any) to compute the CGRect
of the next one. Then, it creates NSTextContainer
with respective size and stores the origin
property of the rectangle in a dedicated array for drawing purposes.
I've also discovered methods [NSLayoutManager firstUnlaidCharacterIndex]
and [NSLayoutManager firstUnlaidGlyphIndex]
but they don't seem to work as expected. After laying out three columns worth of text in only one column, they returned the length of the entire string and not the position of the first character which didn't fit into the first column. That's why I rather used the range-based approach.
That's all folks, be safe! Pete.