首页 文章

NSAttributedString背景颜色和圆角

提问于
浏览
45

我有一个关于自定义 UIView 的圆角和文本背景颜色的问题 .

基本上,我需要在自定义UIView中实现这样的效果(图像附加 - 注意一侧的圆角):
Background highlight

我认为使用的方法是:

  • 使用核心文本获取字形运行 .

  • 检查高光范围 .

  • 如果当前运行在高亮范围内,请在绘制字形运行之前绘制带圆角和所需填充颜色的背景矩形 .

  • 绘制字形运行 .

但是,我不确定这是否是唯一的解决方案(或者就此而言,这是否是最有效的解决方案) .

使用 UIWebView 不是一个选项,所以我必须在自定义 UIView 中执行此操作 .

我的问题是,这是最好的使用方法,我是否走在正确的轨道上?或者我错过了重要的事情或错误的方式?

1 回答

  • 53

    我设法达到了上述效果,所以我想发布一个相同的答案 .

    如果有人有任何关于使这个更有效的建议,请随时贡献 . 我一定会把你的答案标记为正确答案 . :)

    为此,您需要将"custom attribute"添加到 NSAttributedString .

    基本上,这意味着你可以添加任何键值对,只要它是你可以添加到 NSDictionary 实例的东西 . 如果系统无法识别该属性,则不执行任何操作 . 作为开发人员,您可以为该属性提供自定义实现和行为 .

    出于本答案的目的,我们假设我添加了一个名为 @"MyRoundedBackgroundColor" 的自定义属性,其值为 [UIColor greenColor] .

    对于接下来的步骤,您需要基本了解 CoreText 如何完成工作 . 查看Apple's Core Text Programming Guide以了解帧/行/字形运行/字形等的内容 .

    那么,以下是步骤:

    • 创建自定义UIView子类 .

    • 拥有接受 NSAttributedString 的 property .

    • 使用 NSAttributedString 实例创建 CTFramesetter .

    • 覆盖 drawRect: 方法

    • CTFramesetter 创建 CTFrame 实例 .

    • 您需要提供 CGPathRef 来创建 CTFrame . 使 CGPath 与您希望绘制文本的帧相同 .

    • 获取当前图形上下文并翻转文本坐标系 .

    • 使用 CTFrameGetLines(...) ,获取刚刚创建的 CTFrame 中的所有行 .

    • 使用 CTFrameGetLineOrigins(...) ,获取 CTFrame 的所有行原点 .

    • 开始 for loop - 对于 CTLine 数组中的每一行...

    • 使用 CGContextSetTextPosition(...) 将文本位置设置为 CTLine 的开头 .

    • 使用 CTLineGetGlyphRuns(...)CTLine 获取所有字形运行( CTRunRef ) .

    • 开始另一个 for loop - 对于 CTRun 数组中的每个glyphRun ...

    • 使用 CTRunGetStringRange(...) 获取运行范围 .

    • 使用 CTRunGetTypographicBounds(...) 获取印刷边界 .

    • 使用 CTLineGetOffsetForStringIndex(...) 获取运行的x偏移量 .

    • 使用上述函数返回的值计算边界矩形(让我们称之为 runBounds ) .

    • 记住 - CTRunGetTypographicBounds(...) 需要指向变量的指针来存储文本的"ascent"和"descent" . 您需要添加它们才能获得运行高度 .

    • 使用 CTRunGetAttributes(...) 获取运行的属性 .

    • 检查属性字典是否包含您的属性 .

    • 如果属性存在,则计算需要绘制的矩形的边界 .

    • 核心文本的行起源位于基线 . 我们需要从文本的最低点到最高点 . 因此,我们需要调整下降 .

    • 因此,从我们在步骤16中计算的边界矩减去下降( runBounds ) .

    • 现在我们有 runBounds ,我们知道我们想要绘制哪个区域 - 现在我们可以使用任何 CoreGraphis / UIBezierPath 方法来绘制和填充具有特定圆角的矩形 .

    • UIBezierPath 有一个名为 bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: 的便捷类方法,让你绕过特定的角落 . 您可以在第二个参数中使用位掩码指定角点 .

    • 现在您已经填充了矩形,只需使用 CTRunDraw(...) 绘制字形运行 .

    • 庆祝创建自定义属性的胜利 - 喝啤酒什么的! :d

    关于检测属性范围是否超过多次运行,您可以获得自定义属性的整个有效范围当第一次运行遇到属性时 . 如果您发现属性的最大有效范围的长度大于运行的长度,则需要在右侧绘制尖角(对于从左到右的脚本) . 更多的数学运算将让您检测下一行的高光转角样式 . :)

    附件是效果的屏幕截图 . 顶部的框是标准 UITextView ,我为其设置了attributionText . 底部的框是使用上述步骤实现的框 . 已为textViews设置了相同的属性字符串 .
    custom attribute with rounded corners

    再说一次,如果有比我使用的方法更好的方法,请告诉我! :d

    希望这有助于社区 . :)

    干杯!

相关问题