NSAttributedString 背景色和圆角 [英] NSAttributedString background color and rounded corners

查看:67
本文介绍了NSAttributedString 背景色和圆角的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a question regarding rounded corners and text background color for a custom UIView.

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

Basically, I need to achieve an effect like this (image attached - notice the rounded corners on one side) in a custom UIView:

我认为使用的方法是:

  • 使用核心文本获取字形运行.
  • 检查高亮范围.
  • 如果当前运行在高亮范围内,请在绘制字形运行之前绘制带圆角和所需填充颜色的背景矩形.
  • 绘制字形运行.

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

However, I'm not sure whether this is the only solution (or for that matter, whether this is the most efficient solution).

使用 UIWebView 不是一种选择,所以我必须在自定义 UIView 中进行.

Using a UIWebView is not an option, so I have to do it in a custom UIView.

我的问题是,这是最好的使用方法吗,我是否在正确的轨道上?还是我错过了一些重要的事情或以错误的方式去做?

My question being, is this the best approach to use, and am I on the right track? Or am I missing out something important or going about it the wrong way?

推荐答案

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

I managed to achieve the above effect, so thought I'd post an answer for the same.

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

If anyone has any suggestions about making this more effective, please feel free to contribute. I'll be sure to mark your answer as the correct one. :)

为此,您需要向 NSAttributedString 添加自定义属性".

For doing this, you'll need to add a "custom attribute" to NSAttributedString.

基本上,这意味着您可以添加任何键值对,只要您可以将其添加到 NSDictionary 实例中即可.如果系统无法识别该属性,则不会执行任何操作.作为开发者,您有责任为该属性提供自定义实现和行为.

Basically, what that means is that you can add any key-value pair, as long as it is something that you can add to an NSDictionary instance. If the system does not recognize that attribute, it does nothing. It is up to you, as the developer, to provide a custom implementation and behavior for that attribute.

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

For the purposes of this answer, let us assume I've added a custom attribute called: @"MyRoundedBackgroundColor" with a value of [UIColor greenColor].

对于接下来的步骤,您需要对 CoreText 如何完成工作有一个基本的了解.查看 Apple 的核心文本编程指南,用于了解什么是框架/行/字形运行/字形等.

For the steps that follow, you'll need to have a basic understanding of how CoreText gets stuff done. Check out Apple's Core Text Programming Guide for understanding what's a frame/line/glyph run/glyph, etc.

所以,这里是步骤:

  1. 创建自定义 UIView 子类.
  2. 具有用于接受 NSAttributedString 的属性.
  3. 使用该 NSAttributedString 实例创建一个 CTFramesetter.
  4. 覆盖drawRect:方法
  5. CTFramesetter 创建一个 CTFrame 实例.
  1. Create a custom UIView subclass.
  2. Have a property for accepting an NSAttributedString.
  3. Create a CTFramesetter using that NSAttributedString instance.
  4. Override the drawRect: method
  5. Create a CTFrame instance from the CTFramesetter.
  1. 您需要提供一个 CGPathRef 来创建 CTFrame.使 CGPath 与您希望在其中绘制文本的框架相同.
  1. You will need to give a CGPathRef to create the CTFrame. Make that CGPath to be the same as the frame in which you wish to draw the text.

  • 获取当前图形上下文并翻转文本坐标系.
  • 使用 CTFrameGetLines(...),获取您刚刚创建的 CTFrame 中的所有行.
  • 使用 CTFrameGetLineOrigins(...),获取 CTFrame 的所有行起点.
  • 开始一个 for 循环 - 对于 CTLine...
  • 数组中的每一行
  • 使用 CGContextSetTextPosition(...) 将文本位置设置为 CTLine 的开头.
  • 使用 CTLineGetGlyphRuns(...)CTLine 获取所有 Glyph Runs (CTRunRef).
  • 开始另一个 for 循环 - 对于 CTRun... 数组中的每个 glyphRun
  • 使用 CTRunGetStringRange(...) 获取运行范围.
  • 使用 CTRunGetTypographicBounds(...) 获取印刷边界.
  • 使用 CTLineGetOffsetForStringIndex(...) 获取运行的 x 偏移量.
  • 使用从上述函数返回的值计算边界矩形(我们称之为 runBounds).

  • Get the current graphics context and flip the text coordinate system.
  • Using CTFrameGetLines(...), get all the lines in the CTFrame you just created.
  • Using CTFrameGetLineOrigins(...), get all the line origins for the CTFrame.
  • Start a for loop - for each line in the array of CTLine...
  • Set the text position to the start of the CTLine using CGContextSetTextPosition(...).
  • Using CTLineGetGlyphRuns(...) get all the Glyph Runs (CTRunRef) from the CTLine.
  • Start another for loop - for each glyphRun in the array of CTRun...
  • Get the range of the run using CTRunGetStringRange(...).
  • Get typographic bounds using CTRunGetTypographicBounds(...).
  • Get the x offset for the run using CTLineGetOffsetForStringIndex(...).
  • Calculate the bounding rect (let's call it runBounds) using the values returned from the aforementioned functions.

    1. 记住 - CTRunGetTypographicBounds(...) 需要指向变量的指针来存储文本的上升"和下降".您需要添加这些以获得运行高度.
    1. Remember - CTRunGetTypographicBounds(...) requires pointers to variables to store the "ascent" and "descent" of the text. You need to add those to get the run height.

  • 使用 CTRunGetAttributes(...) 获取运行的属性.
  • 检查属性字典是否包含您的属性.
  • 如果您的属性存在,请计算需要绘制的矩形的边界.
  • 核心文本的行起点位于基线处.我们需要从文本的最低点绘制到最高点.因此,我们需要调整下降.
  • 因此,从我们在步骤 16 中计算的边界矩形中减去下降 (runBounds).
  • 现在我们有了 runBounds,我们知道要绘制哪个区域 - 现在我们可以使用任何 CoreGraphis/UIBezierPath使用特定圆角绘制和填充矩形的方法.

  • Get the attributes for the run using CTRunGetAttributes(...).
  • Check if the attribute dictionary contains your attribute.
  • If your attribute exists, calculate the bounds of the rectangle that needs to be painted.
  • Core text has the line origins at the baseline. We need to draw from the lowermost point of the text to the topmost point. Thus, we need to adjust for descent.
  • So, subtract the descent from the bounding rect that we calculated in step 16 (runBounds).
  • Now that we have the runBounds, we know what area we want to paint - now we can use any of the CoreGraphis/UIBezierPath methods to draw and fill a rect with specific rounded corners.

    1. UIBezierPath 有一个名为 bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: 的便利类方法,它可以让你圆特定的角.您可以在第二个参数中使用位掩码指定角点.
    1. UIBezierPath has a convenience class method called bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: that let's you round specific corners. You specify the corners using bit masks in the 2nd parameter.

  • 既然您已经填充了矩形,只需使用 CTRunDraw(...) 绘制字形运行.
  • 庆祝您创建自定义属性的胜利 - 喝啤酒什么的!:D
  • 关于检测属性范围扩展到多次运行,当第一次运行遇到该属性时,您可以获得自定义属性的整个有效范围.如果你发现你的属性的最大有效范围的长度大于你的run长度,你需要在右侧画尖角(对于从左到右的脚本).更多的数学运算也可以让您检测下一行的高光角样式.:)

    Regarding detecting that the attribute range extends over multiple runs, you can get the entire effective range of your custom attribute when the 1st run encounters the attribute. If you find that the length of the maximum effective range of your attribute is greater than the length of your run, you need to paint sharp corners on the right side (for a left to right script). More math will let you detect the highlight corner style for the next line as well. :)

    附上效果截图.顶部的框是一个标准的UITextView,我已经为其设置了attributedText.底部的框是使用上述步骤实现的框.为两个 textViews 设置了相同的属性字符串.

    Attached is a screenshot of the effect. The box on the top is a standard UITextView, for which I've set the attributedText. The box on the bottom is the one that has been implemented using the above steps. The same attributed string has been set for both the textViews.

    同样,如果有比我使用过的方法更好的方法,请告诉我!:D

    Again, if there is a better approach than the one that I've used, please do let me know! :D

    希望这对社区有所帮助.:)

    Hope this helps the community. :)

    干杯!

    这篇关于NSAttributedString 背景色和圆角的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆