0

I want to implement a UITextView whose content consists of plain text and tags, where the tags are text with a rounded gray background.

I tried to use NSAttributedString, but it cannot set rounded background.

Any idea? Thanks in advance.

enter image description here

0

2 Answers 2

1

You can implement by generating an Image using CoreGraphics and wrapping it inside a NSTextAttachment.

For example, here's an implementation as an extension of String:

extension String {
    func generateImage(_ size: CGSize, 
                       textFont: UIFont = .systemFont(ofSize: 16),
                       textColor: UIColor = .white, 
                       fillColor: UIColor = .brown) -> NSAttributedString {
        let format = UIGraphicsImageRendererFormat()
        format.scale = UIScreen.main.scale
        let render = UIGraphicsImageRenderer(size: size, format: format)
        
        let image = render.image { context in
            let ellipsePath = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: size.height / 2).cgPath
            context.cgContext.setFillColor(fillColor.cgColor)
            context.cgContext.addPath(ellipsePath)
            context.cgContext.fillPath()
            let attributed = NSAttributedString(string: self, attributes: [.font: textFont, .foregroundColor: textColor])
            let textSize = attributed.size()
            attributed.draw(at: CGPoint(x: (size.width - textSize.width) / 2, y: (size.height - textSize.height) / 2))
        }
        let attachment = NSTextAttachment(data: nil, ofType: nil)
        attachment.image = image
        attachment.bounds = .init(x: 0, y: -9.3125, width: size.width, height: size.height)
        attachment.lineLayoutPadding = 5
        return .init(attachment: attachment)
    }
}

Then when you want to use it:


// Your UITextView with an attributed string
let view = UITextView()
view.attributedText = testAttributedString()

// An example usage
func testAttributedString() -> NSAttributedString {
    let test = NSMutableAttributedString()
    test.append(.init(string: "How"))
    test.append("are".generateImage(.init(width: 60, height: 30)))
    test.append(.init(string: "you"))
    return test
}

Here's an example on how it looks

Sign up to request clarification or add additional context in comments.

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
1

From iOS 17 the textItemTag was added, and in iOS 18 the textHighlightColor and textHighlightStyle were added.

Adding textItemTag allows attaching a custom piece of information for your own usage (e.g a UUID of the tag), where the textHighlight ones are for adding a bordered background and foreground colors.

You can use them with AttributedString, or, preferably NSAttributedString if you intend to use it in conjunction with a UITextView:

// Some attributed string
let txt = NSMutableAttributedString(string: "Eat launch for ", attributes: [.font: UIFont.systemFont(ofSize: 16.0)])

// The tag
let tag = NSAttributedString(string: "24 Minutes", attributes: [
            .textItemTag: "time:24m", // Your custom tag
            .font: UIFont.systemFont(ofSize: 16.0),
            .textHighlightColorScheme: NSAttributedString.TextHighlightColorScheme.mint,
            .textHighlightStyle: NSAttributedString.TextHighlightStyle.default
        ])

// Join them together
txt.append(tag)

When implementing UITextViewDelegate you offer additional actions or respond to clicks using:

func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? {
    UIAction(handler: { (_) in print("Clicked on \(textItem.content)") })
}

1 Comment

Nice! Didn't know about .textHighlightColorScheme. It's kinda weird we can't use a custom color (and still only iOS 18+..)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.