I noticed that iOS 7 introduces new classes related to text layout such as NSLayoutManager, NSTextStorage, and NSTextContainer. How can I use these in order to get information about word wrapping on an NSString?
For example, say I have a long NSString which I put in a UILabel. If I enable multiple lines on the UILabel, it would produce a string such as the following:
The quick brown fox jumps
over the lazy dog.
That's great, but I can't access the line breaks in code (e.g. after the word jumps
I would want it to return \n
or something similar). I would want to know at which character indexes the line breaks occur. I know we can do this with CoreText, but since we have these new classes in iOS 7, I was wondering how we can use them instead.
Example:
CGFloat maxWidth = 150;
NSAttributedString *s =
[[NSAttributedString alloc]
initWithString:@"The quick brown fox jumped over the lazy dog."
attributes:@{NSFontAttributeName:[UIFont fontWithName:@"GillSans" size:20]}];
NSTextContainer* tc =
[[NSTextContainer alloc] initWithSize:CGSizeMake(maxWidth,CGFLOAT_MAX)];
NSLayoutManager* lm = [NSLayoutManager new];
NSTextStorage* tm = [[NSTextStorage alloc] initWithAttributedString:s];
[tm addLayoutManager:lm];
[lm addTextContainer:tc];
[lm enumerateLineFragmentsForGlyphRange:NSMakeRange(0,lm.numberOfGlyphs)
usingBlock:^(CGRect rect, CGRect usedRect,
NSTextContainer *textContainer,
NSRange glyphRange, BOOL *stop) {
NSRange r = [lm characterRangeForGlyphRange:glyphRange actualGlyphRange:nil];
NSLog(@"%@", [s.string substringWithRange:r]);
}];
swift translation:
guard let font1: UIFont = textView.font else { return }
var lines: [String] = []
let maxWidth: CGFloat = textView.frame.width
let s: NSAttributedString = NSAttributedString.init(string: textView.text, attributes: [.font: font1])
let tc: NSTextContainer = NSTextContainer.init(size: CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude))
let lm: NSLayoutManager = NSLayoutManager.init()
let tm: NSTextStorage = NSTextStorage.init(attributedString: s)
tm.addLayoutManager(lm)
lm.addTextContainer(tc)
lm.enumerateLineFragments(forGlyphRange: NSRange(location: 0, length: lm.numberOfGlyphs)) { (rect: CGRect, usedRect: CGRect, textContainer: NSTextContainer, glyphRange: NSRange, Bool) in
let r: NSRange = lm.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
let str = s as NSAttributedString
let s2 = str.attributedSubstring(from: r)
print(s2)
lines.append(s2.string)
}