Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Customize underline pattern in NSAttributedString (iOS7+)

I'm trying to add a dotted underline style to an NSAttributedString (on iOS7+/TextKit). Of course, I tried the built-in NSUnderlinePatternDot:

NSString *string = self.label.text;
NSRange underlineRange = [string rangeOfString:@"consetetur sadipscing elitr"];

NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:string];
[attString addAttributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot)} range:underlineRange];

self.label.attributedText = attString;

However, the style produced by this is actually rather dashed than dotted:

screenshot

Am I missing something obvious here (NSUnderlinePatternReallyDotted? ;) ) or is there perhaps a way to customize the line-dot-pattern?

like image 496
Daniel Rinser Avatar asked Aug 27 '14 09:08

Daniel Rinser


1 Answers

I've spent a little bit of time playing around with Text Kit to get this and other similar scenarios to work. The actual Text Kit solution for this problem is to subclass NSLayoutManager and override drawUnderline(forGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:)

This is the method that gets called to actually draw the underline that is requested by the attributes in the NSTextStorage. Here's a description of the parameters that get passed in:

  • glyphRange the index of the first glyph and the number of following glyphs to be underlined
  • underlineType the NSUnderlineStyle value of the NSUnderlineAttributeName attribute
  • baselineOffset the distance between the bottom of the bounding box and the baseline offset for the glyphs in the provided range
  • lineFragmentRect the rectangle that encloses the entire line that contains glyphRange
  • lineFragmentGlyphRange the glyph range that makes up the entire line that contains glyphRange
  • containerOrigin the offset of the container within the text view to which it belongs

Steps to draw underline:

  1. Find the NSTextContainer that contains glyphRange using NSLayoutManager.textContainer(forGlyphAt:effectiveRange:).
  2. Get the bounding rectangle for glyphRange using NSLayoutManager.boundingRect(forGlyphRange:in:)
  3. Offset the bounding rectangle by the text container origin using CGRect.offsetBy(dx:dy:)
  4. Draw your custom underline somewhere between the bottom of the offset bounding rectangle and the baseline (as determined by baselineOffset)
like image 156
Dave Weston Avatar answered Sep 22 '22 06:09

Dave Weston