#Background
Apple’s Mail app for iOS seems to treat a single email address as a single character once entered. When you try to edit the text, the whole email is selected.
In an attempt to mimic the email entry behaviour of the Mail app, I came upon an issue when rendering instances of NSTextAttachment
inside a UITextView
.
#The Problem
To accomplish this, I attempted to take snapshots of the entered text, and render it as a text attachment inline with any additional text (I might write another post about the data source behaviour another time).
The technique worked very well, except I found my text baseline was being thrown around a bit.
This had me stumped for a while. The UIImage
that I had generated for the NSTextAttachment
was simply a UILabel
rendered offscreen, and then sized to fit. Given that all my font sizes and string attributes matched up, there should not be any reason for the baselines to not line up perfectly.
I set a background colour on the label to investigate:
Aha! It looks like by default, the UITextView
renders instances of NSTextAttachment
on the baseline, extending upwards. This means the larger the image, the further down my baseline of the following text will be pushed.
You can see the snapshotted UILabel
has space at the bottom for it’s descenders, so we don’t want it rendering on the baseline.
#The Solution
After spending far too much time delving into various TextKit classes, I came up with a fairly straightforward solution.
I started checking out the NSLayoutManager
class, as that seemed to control how glyphs and characters are positioned in a text container. However it turns out that the NSTextAttachment
class already has a method returning it’s bounds:
So, I made a simple subclass of NSTextAttachment
which exposes a property for adjusting the y-offset of the attachment bounds.
Using this subclass now, I can grab the descender value from the font I’m using in my attributed string.
And bingo! The descender height was exactly the offset I needed to get all my NSTextAttachment
instances to line up nicely with my existing text.