我有一个关于圆角和自定义文字背景色问题UIView
。
基本上,我需要实现这样的效果(附加图像 - 注意到在一侧圆角)在一个自定义的UIView:
我想使用的方法是:
- 使用核心文本得到字形运行。
- 检查加亮区范围。
- 如果当前游程是高亮范围内时,绘制字形运行之前绘制一个带圆角和所需的填充颜色的背景矩形。
- 绘制字形运行。
但是,我不知道这是否是唯一的解决办法(或就此而言,这是否是最有效的解决方案)。
使用UIWebView
是不是一种选择,所以我必须做一个定制UIView
。
我的问题的存在,这就是用最好的方法,而我是在正确的轨道上? 还是我失去了一些重要的东西或去约了错误的方式?
我设法达到上述效果,所以以为我会发布了同样的答案。
如果任何人有关于使这个更有效的任何建议,请随时作出贡献。 我一定会来标记你的答案是正确的。 :)
这样做,你需要一个“自定义属性”添加到NSAttributedString
。
基本上,这是什么意思是,你可以添加任何键值对,只要它的东西,你可以添加到一个NSDictionary
实例。 如果系统无法识别的属性,它什么都不做。 它是你的,作为开发商,提供该属性的定制实现和行为。
对于这个答案的目的,让我们假设我也添加了一个自定义属性: @"MyRoundedBackgroundColor"
与值[UIColor greenColor]
。
对于接下来的步骤中,你需要有一个如何一个基本的了解CoreText
得到的东西做。 看看苹果的核心文本编程指南理解什么是帧/线/字形运行/字形等。
所以,这里的步骤:
- 创建一个自定义UIView子类。
- 对受理的属性
NSAttributedString
。 - 创建
CTFramesetter
使用NSAttributedString
实例。 - 重写
drawRect:
方法 - 创建
CTFrame
从实例CTFramesetter
。 - 你需要给出一个
CGPathRef
创建CTFrame
。 作出这样的CGPath
是一样的,你要绘制文本框。
- 获取当前图形上下文和翻转文字的坐标系。
- 使用
CTFrameGetLines(...)
获得在所有行CTFrame
刚刚创建。 - 使用
CTFrameGetLineOrigins(...)
让所有的行起源CTFrame
。 - 启动一个
for loop
-对于数组中的每一行CTLine
... - 设置文本位置到开始
CTLine
使用CGContextSetTextPosition(...)
- 使用
CTLineGetGlyphRuns(...)
获得所有铭文奔跑( CTRunRef
从) CTLine
。 - 启动另一个
for loop
的阵列中的每个glyphRun - CTRun
... - 获取使用运行的范围
CTRunGetStringRange(...)
- 获取使用印刷界
CTRunGetTypographicBounds(...)
- 看一下X使用运行偏移
CTLineGetOffsetForStringIndex(...)
- 计算边界矩形(姑且称之为
runBounds
)使用来自上述函数的返回值。 - 记住-
CTRunGetTypographicBounds(...)
需要指针变量来存储“上升”和文本的“血统”。 您需要添加这些获得运行高度。
- 获得使用运行属性
CTRunGetAttributes(...)
- 检查属性字典包含您的属性。
- 如果你的属性存在,计算出需要绘制矩形的边界。
- 核心文本有在基准线的起源。 我们需要从文本到最高点的最低点绘制。 因此,我们需要调整的后裔。
- 因此,减去我们在步骤16(计算的边界RECT下降
runBounds
)。 - 现在,我们有
runBounds
,我们知道我们要画什么区-现在我们可以使用任何的CoreGraphis
/ UIBezierPath
方法绘制并填写具体的圆角一个矩形。 -
UIBezierPath
有一个称为方便类方法bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
它可以让您全面具体的角落。 您指定使用位掩码在第二个参数的角落。
- 现在,你已经填补了矩形,简单地画使用运行字形
CTRunDraw(...)
- 为庆祝在创建了您的自定义属性的胜利 - 喝啤酒什么的! :d
对于检测到的属性范围扩展多次运行,则可以在第一次运行遇到属性得到整个有效范围自定义属性。 如果您发现最大有效射程你的属性的长度比你跑的长度,你需要画右边尖角(对于从左到右脚本)。 更多的数学会让你发现的亮点角样式下一行也是如此。 :)
附上的效果截图。 在箱体顶部是一个标准UITextView
,为此,我已经设置了attributedText。 底部的箱是已经使用上述步骤实现的一个。 同样的属性串已被设置为两个textViews。
同样,如果有比我使用了一个更好的办法,请不要让我知道! :d
希望这有助于社会。 :)
干杯!