Search code examples
iosswiftnsattributedstringios15attributedstring

How do you add an image attachment to an AttributedString?


I'm working to replace NSAttributedString with AttributedString but have been unsuccessful in getting attachments to work. The image doesn't appear in the string despite the fact I applied the attachment.

let textAttachment = NSTextAttachment(image: UIImage(systemName: "exclamationmark.triangle.fill")!)
textAttachment.accessibilityLabel = "Warning"

// Original code
label.attributedText = NSAttributedString(attachment: textAttachment)

// New code
var attributedString = AttributedString()
attributedString.attachment = textAttachment
label.attributedText = NSAttributedString(attributedString)

Solution

  • NSAttributedString(attachment:) magically creates an NSAttributedString with a single character (NSAttachmentCharacter which is U+FFFC OBJECT REPLACEMENT CHARACTER) and applies the text attachment attribute in order to replace that character with the image.

    With the new AttributedString API you'll need to manually replicate that:

    let textAttachment = NSTextAttachment(image: UIImage(systemName: "exclamationmark.triangle.fill")!)
    textAttachment.accessibilityLabel = "Warning"
    
    let attributedString = AttributedString("\(UnicodeScalar(NSTextAttachment.character)!)", attributes: AttributeContainer.attachment(textAttachment))
    
    label.attributedText = NSAttributedString(attributedString)
    

    Here's an example that replaces a substring with an image:

    let addString = "+"
    let string = "Tap \(addString) to add a task."
    let addTextAttachment = NSTextAttachment(image: UIImage(systemName: "plus.square")!)
    
    // NSAttributedString
    label.attributedText = {
        let attributedString = NSMutableAttributedString(string: string)
        attributedString.replaceCharacters(in: (attributedString.string as NSString).range(of: addString), with: NSAttributedString(attachment: addTextAttachment))
        return attributedString
    }()
    
    // AttributedString
    label.attributedText = {
        var attributedString = AttributedString(string)
        let attachmentString = AttributedString("\(UnicodeScalar(NSTextAttachment.character)!)", attributes: AttributeContainer.attachment(addTextAttachment))
        attributedString.replaceSubrange(attributedString.range(of: addString)!, with: attachmentString)
        return NSAttributedString(attributedString)
    }()