Search code examples
iosswiftuilabelnsattributedstringnstextattachment

Replacing emoji within UILabel with NSTextAttachment - not able to set the plaintext representation back to regular emoji for copying/pasting


I'm replacing emoji within a UILabel/UITextView with my own custom emoji. This works fine, but what I'm struggling with is getting the original emoji to paste.

So for example where the UILabel may display "Hello 😀", with a custom emoji, copying then pasting that text, or calling the text property of the UILabel would display "Hello 😀" with the standard emoji.

I'm implementing this using a subclass of NSTextAttachment which displays an image, and adding it as an NSAttributedString whenever the emoji is used:

class EmojiTextAttachment: NSTextAttachment {
    static let size = CGSize(width: 16, height: 16)

    let emoji: Emoji

    init(emoji: Emoji) {
        self.emoji = emoji

        super.init(data: emoji.rawValue.data(using: .utf8), ofType: "public.text")
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func image(forBounds imageBounds: CGRect, textContainer: NSTextContainer?, characterIndex charIndex: Int) -> UIImage? {
        return Emoji.generateConstrainedFaceImage(emoji: emoji, height: imageBounds.size.height)
    }
}

let emoji = Emoji(rawValue: emojiString)!
let emojiTextAttachment = EmojiTextAttachment(emoji: emoji)
emojiTextAttachment.bounds = CGRect(x: 0, y: self.font!.descender, width: self.font!.lineHeight, height: self.font!.lineHeight)    
let attributedString = NSAttributedString(attachment: emojiTextAttachment)

This appears correctly, but now if I call the text property of the UITextField, it doesn't include the NSTextAttachment as part of its' text. Additionally, copying the content copies the EmojiTextAttachment as data. Here, I've tried to set the data:ofType: to be a string (converted to data), but that didn't work, it just forms some data which is pated. And setting the image through emojiTextAttachment.image means it'd paste the image. Setting it through the imageForBounds:textContainer:characterIndex: function means the image itself doesn't get copied/pasted.

Is there a way that I can set the plaintext representation of an NSTextAttachment, or some other known way of making this work?


Solution

  • Couldn't find a really clean way of doing it. Subclassing NSAttributedString isn't supported, so no way of overriding the string function.

    Instead, I've added an extension of NSAttributedString with a plaintextRepresentation function, which enumerates through the attributes, replacing any EmojiTextAttachments with the emoji itself, then returns the string value of the attributed string.