Swift中使用NSAttributedString无法绘制圆角描边的问题求助
Swift实现文本圆角描边方案
NSMutableAttributedString没有直接提供圆角描边的属性键,系统默认的strokeWidth和strokeColor是基于字体原始字形路径绘制,无法生成圆角效果。要实现圆角描边,需要通过自定义绘制文本路径并添加圆角处理来完成,以下是一种可行的实现方式:
自定义UILabel子类实现圆角描边
创建继承自UILabel的子类,重写draw(_ rect: CGRect)方法,借助CoreText获取文本字形路径,通过设置路径的圆角连接属性实现圆角描边:
import UIKit class RoundedStrokeLabel: UILabel { /// 描边宽度 var strokeWidth: CGFloat = 2.0 /// 描边颜色 var strokeColor: UIColor = .black override func draw(_ rect: CGRect) { guard let text = text, let font = font else { return } // 构建带字体属性的富文本 let attributedString = NSMutableAttributedString(string: text) attributedString.addAttribute(.font, value: font, range: NSRange(location: 0, length: text.count)) // 初始化CoreText排版相关对象 let framesetter = CTFramesetterCreateWithAttributedString(attributedString as CFAttributedString) let textPath = CGPath(rect: bounds, transform: nil) let textFrame = CTFramesetterCreateFrame(framesetter, CFRange(location: 0, length: attributedString.length), textPath, nil) let context = UIGraphicsGetCurrentContext() context?.saveGState() // 转换坐标系适配UIKit context?.translateBy(x: 0, y: bounds.height) context?.scaleBy(x: 1.0, y: -1.0) // 获取文本行与行原点 let lines = CTFrameGetLines(textFrame) as [CTLine] var lineOrigins = [CGPoint](repeating: .zero, count: lines.count) CTFrameGetLineOrigins(textFrame, CFRange(location: 0, length: 0), &lineOrigins) // 遍历每行处理字形路径 for (lineIndex, line) in lines.enumerated() { let runs = CTLineGetGlyphRuns(line) as [CTRun] for run in runs { let runFont = CTRunGetAttributes(run)[kCTFontAttributeName] as! CTFont let glyphCount = CTRunGetGlyphCount(run) var glyphs = [CGGlyph](repeating: 0, count: glyphCount) var positions = [CGPoint](repeating: .zero, count: glyphCount) CTRunGetGlyphs(run, CFRange(location: 0, length: 0), &glyphs) CTRunGetPositions(run, CFRange(location: 0, length: 0), &positions) // 处理每个字形的圆角描边 for glyphIndex in 0..<glyphCount { let glyph = glyphs[glyphIndex] var position = positions[glyphIndex] position.y += lineOrigins[lineIndex].y guard let glyphPath = CTFontCreatePathForGlyph(runFont, glyph, nil) else { continue } // 转换字形路径到当前排版位置 let transformedPath = glyphPath.copy(transform: CGAffineTransform(translationX: position.x, y: position.y)) // 生成带圆角连接的描边路径 let roundedStrokePath = transformedPath.copy( strokingWithWidth: strokeWidth, lineCap: .round, lineJoin: .round, miterLimit: 0 ) // 绘制圆角描边 strokeColor.setStroke() context?.addPath(roundedStrokePath) context?.setLineWidth(strokeWidth) context?.drawPath(using: .stroke) } } } context?.restoreGState() // 绘制内部填充文本 super.draw(rect) } }
使用示例
直接实例化自定义子类并设置属性:
let roundedLabel = RoundedStrokeLabel() roundedLabel.text = "Swift圆角描边" roundedLabel.font = UIFont.systemFont(ofSize: 32, weight: .bold) roundedLabel.strokeWidth = 3.0 roundedLabel.strokeColor = .systemBlue roundedLabel.textColor = .white roundedLabel.frame = CGRect(x: 40, y: 150, width: 300, height: 80) view.addSubview(roundedLabel)
关键说明
- 核心是通过
CGPath.copy(strokingWithWidth:lineCap:lineJoin:miterLimit:)方法,将lineJoin设置为.round,让路径转角处呈现圆角 - 借助CoreText可以精准获取每个字形的路径,确保所有文本转角都能应用圆角处理
- 这种方式相比系统默认描边,能完全控制描边的圆角效果,适配所有字体
内容的提问来源于stack exchange,提问作者Jabed Dhali
相关产品推荐
相关产品推荐

