ios – Multiline Text Issue with NSAttributedString containing NSTextAttachment


I’m trying to add a rounded background with padding to attributed text. While I’ve successfully implemented the rounded background with padding, I’m facing challenges in aligning the attachment precisely with the text.

final class TextUViewController: UIViewController {
    private lazy var label: UILabel = {
        let label = UILabel()
        label.numberOfLines = 2
        label.backgroundColor = .red.withAlphaComponent(0.3)
        return label
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        view.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15).isActive = true
        label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15).isActive = true
        label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        label.attributedText = createAttributedText()
    }

    func createAttributedText() -> NSAttributedString {
        let text = "It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old."
        let token = "2000"
        let attributedString = NSMutableAttributedString(string: text)
        let baseAttributes: [NSAttributedString.Key: Any] = [
            .foregroundColor: UIColor.green,
            .font: UIFont.systemFont(ofSize: 14, weight: .medium),
            .baselineOffset: 0,
        ]
        let baseRange = NSRange(location: 0, length: attributedString.length)
        attributedString.addAttributes(baseAttributes, range: baseRange)

        if let range = text.range(of: token) {
            let nsRange = NSRange(range, in: text)
            let attachment = createAttachment()
            let attachmentString = NSAttributedString(attachment: attachment)
            attributedString.replaceCharacters(in: nsRange, with: attachmentString)
        }

        return attributedString
    }

    func createAttachment() -> NSTextAttachment {
        let label = UILabel()
        label.text = "2000"
        label.textColor = .blue
        label.backgroundColor = .white
        label.font = UIFont.systemFont(ofSize: 14, weight: .bold)
        label.layer.cornerRadius = 2
        label.layer.masksToBounds = true
        label.textAlignment = .center
        label.sizeToFit()

        let width = label.bounds.width + 12
        let height = label.bounds.height + 6
        label.bounds = .init(
            x: 0,
            y: 0,
            width: width,
            height: height
        )

        let renderer = UIGraphicsImageRenderer(size: label.bounds.size)
        let image = renderer.image { context in
            label.layer.render(in: context.cgContext)
        }

        let attachment = NSTextAttachment()
        attachment.image = image

        return attachment
    }
}

Do you have any suggestions to solve this problem, or do you have an alternative solution for adding a rounded background to attributed text?

I’ve tried using baselineOffset to fix the alignment issue, but then the text doesn’t behave correctly in multiline scenarios.

with baselineOffset: 0 multi line

with baselineOffset: 0 multi line

with baselineOffset: 6 multi line

with baselineOffset: 6 multi line

with baselineOffset: 6 single line (This is what I want to achieve in both single and multiline texts.)

with baselineOffset: 6 single line

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img