iostagsuitextviewnsattributedstring

iOS UITextView rich text with tags


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


Solution

  • 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