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?
It returns a character range.
NSAttributedString
doesn't know anything directly about glyphs. Converting characters to glyphs can't happen until layout is performed, andNSAttributedString
doesn't include layout information. For example, anNSAttributedString
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.