Today I learned another difference between iOS and MacOS: Dealing with attachments (specifically, images) in rich text. This is something I’m adding for my Permanent Marker project, which is a multi-platform UIKit / AppKit application.
I had my first exposure working with attachments when working on my Library Notes application. Library Notes does not have full rich text editing, but it does support embedded images, and that’s where I learned the following simple recipe for adding an image to rich text on iOS:
- Create an
NSTextAttachmentthat contains the data for your image.
- Create an
NSAttributedStringthat displays the attachment and insert it into the appropriate location in the rest of your rich text. (Rich text attachments are stored as an attribute on a
NSTextAttachment.character. While you can manually create this by inserting the character and applying the right attribute, it’s easier to use the
Now often I find myself trying to insert photographs into text, which presents a problem: Modern digital photos (even ones from cell phone cameras) are huge. Consequently, I’m almost never working with raw image data from disk or the network for my attachments; instead, I use CoreImage to resize the image before including it as an attachment. This works fine on iOS;
NSTextAttachment has an
image property that I can use for my resized image.
Things are a little different on MacOS. First, and a little surprisingly to me, the designated initializer for NSTextAttachment on the Mac takes a FileWrapper. When I did initial prototyping of image support on the Mac version of Permanent Marker, this worked fine — I’d create a
FileWrapper for my image file, pass the
FileWrapper to the
NSTextAttachment, and boom, there was an image.
My surprise happened when I started resizing the image before trying to show it. I no longer used
NSTextAttachment(fileWrapper:) and instead used
NSTextAttachment(data:ofType:). Suddenly my images stopped showing up.
And that’s when I learned about a MacOS-specific
attachmentCell. If you actually want to display an attachment on MacOS, you need to create an object that conforms to
NSTextAttachmentCellProtocol and assign it to the
attachmentCell property. The built-in
NSTextAttachmentCell class conforms to the protocol, and you can easily create one for an image using the
NSTextAttachmentCell(imageCell:) initializer. Apparently the
NSTextAttachment(fileWrapper:) initializer takes care of setting up the
attachmentCell for you, so you only need to learn about this trick when you need to manipulate data directly rather than using data straight from a file.
Working on Permanent Marker is giving me quite the education on the differences between UIKit and AppKit!