Does NSAttributedString's enumerateAttribute method's block pass character ranges or glyph ranges?

275 views Asked by At

tl;dr: if I call NSAttributedString's enumerateAttribute:inRange:options:usingBlock: method, does the range argument to the block pass a glyph range or a character range?

I have a text view in my app that needs to behave like Notes.app in that when it's not in edit mode, it should show hyperlinks (and they should be tappable), and it should enter edit mode on tap. UITextView doesn't show hyperlinks when editable, but if it's not editable then it won't gain focus on tap by itself. Until iOS 11 this could be solved by using a UITapGestureRecognizer on the text view to trigger editing, but it seems something (maybe with drag and drop?) changed how gesture recognizers work with UITextView (rdar://33009324).

My new and improved solution is a custom UIGestureRecognizer that fires if it sees a tap that is not on a hyperlink, and making all other recognizers on the text view to require it to fail before firing. This is implemented by processing touches, getting their location in the text view, getting the character index for that point, enumerating the NSLinkAttributeName in the text view's textStorage, and checking if my character index overlaps with any of the hyperlinks I get back. If so, then the user tapped on a link, and this recognizer fails.

However, if the character index of the tap does intersect with a link, I need to make sure the tap actually intersects with the link's rect, because it could be that this link is the last thing in the text view's content and the touch is actually below the last line of text. I can do this by getting the rect for the range of the link and checking if it intersects with my touch point. I can use boundingRectForGlyphRange:inTextContainer: for this, but this leads to my question: is the range I have for the link a character range or a glyph range? Do I need to convert it via glyphRangeForCharacterRange:actualGlyphRange: first?

2

There are 2 answers

0
Rob Napier On BEST ANSWER

It returns a character range. NSAttributedString doesn't know anything directly about glyphs. Converting characters to glyphs can't happen until layout is performed, and NSAttributedString doesn't include layout information. For example, an NSAttributedString doesn't know how wide an area it will be drawn into, and without that, you can't know where line breaks will be, and without that you can't decide where to put ligatures.

0
matt On

NSAttributedString knows nothing glyphs, and yes, if you need to know where the glyphs are in a TextKit stack such as that of a UITextView, you ask the layout manager to convert for you.