swiftnstextattachmentnslayoutmanager

How to remove default image icon in NSTextAttachment?


I have an NSAttributedString that can contain NSTextAttachments. I have subclassed the NSTextAttachment and have a property that stores an error message if there was a problem downloading the attachment's image.

I am also subclassing NSLayoutManager to enumerate over attachments and if an error is present in an attachment, it shows the error message on screen in the middle of a CGRect that I provide as a default. In the simulator this works as planned; however, on a device there is a default icon over the bounds of the CGRect. Please see attached images to understand this default icon. How can I override or get rid of this default icon?

Example of the results in the simulator.

Example of the results on a device.

I have researched how to override this icon being displayed but have not found anything. Any ideas?

My initializers in the NSTextAttachment subclass are very basic:


    override init(data contentData: Data?, ofType uti: String?) {
        super.init(data: contentData, ofType: uti)
    }

    required convenience init?(coder: NSCoder) {
        self.init()
        imageCloudID = (coder.decodeObject(forKey: "imageLocation") as! String)
        setImage()
    }
    public override func encode(with coder: NSCoder) {
        coder.encode(imageCloudID, forKey: "imageLocation")
    }

And then if there is an error downloading the image, it sets the optional error property to that error.

In the NSLayoutManager here is my code:

        // Enumerate attachments and draw a placeholder rectangle with error message if an error exists.
        textStorage.enumerateAttribute(.attachment, in: textStorage.entireRange, options: []) { optValue, range, stop in
            guard let attachment = optValue as? MyImageAttachment else { return }
            
            let glyphRange = self.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
            self.enumerateLineFragments(forGlyphRange: glyphRange) { rect, usedRect, textContainer, suppliedGlyphRange, stop in
                // Get the size of the rectangle we want to show for the attachment placeholder.
                let width = rect.size.width
                let imageRect = CGRect(x: rect.minX + 5, y: rect.minY + 10, width: width - 10, height: width / 2)
                
                <<Create a bezier path to outline the rectangle.>>
                
                // Set the UI message to the error message, if present.
                guard let imageError = attachment.imageError else {
                    // If there is not an error present, exit the function.
                    return
                }
                let errorMessage: String
                switch imageError {
                case .no_Internet:
                    errorMessage = "Internet not available."
                << other error messages >>
                }

                // Show the error message in the placeholder rectangle.
                let messageFont = UIFont.systemFont(ofSize: 16, weight: .light)
                let attributedMessage = NSAttributedString(string: errorMessage, attributes: [NSAttributedString.Key.font: messageFont])
                // Center the message.
                let messageWidth = attributedMessage.size().width
                let startingPoint = (imageRect.width - messageWidth) / 2
                attributedMessage.draw(in: CGRect(x: startingPoint, y: imageRect.midY, width: imageRect.width, height: imageRect.height))
                return
            }
        }

Solution

  • Recently, I had a similar issue. Assigning nil for image in NSTextAttachment did not work for me.

    So this is the workaround I implemented.

            let attachment = NSTextAttachment()
            attachment.image = UIImage()
            return attachment