如何通过更改高亮颜色突出显示CoreText?

时间:2011-07-21 15:56:55

标签: iphone ios uiscrollview core-graphics core-text

我有一个UIView的子类,它实现了绘制CoreText的代码。在应用程序中,UIView是在UIScrollView中绘制的。这是我目前在drawRect中使用的代码:渲染NSAttributedString:

CGContextRef context = UIGraphicsGetCurrentContext();

float viewHeight = self.bounds.size.height;
CGContextTranslateCTM(context, 0, viewHeight);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0, 1.0));

CGMutablePathRef path = CGPathCreateMutable();
CGRect bounds = CGRectMake(PADDING_LEFT, -PADDING_TOP, self.bounds.size.width-20.0, self.bounds.size.height);
CGPathAddRect(path, NULL, bounds);

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFMutableAttributedStringRef)attrString);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
CFRelease(framesetter);
CFRelease(path);
CTFrameDraw(frame, context);

在我的NSAttributedString中,我有一些文本属性来为文本着色。

我需要帮助的是概念上,应该如何实现?我对绘制框架,图层等所有各种方法感到困惑。我试图查看几本书和其他资源,但我觉得我缺少一些基本的东西。

以下是我的要求:

  1. 显然需要在更改方向时重新绘制到新位置。
  2. 突出显示颜色需要能够以个别突出显示的方式以编程方式进行更改。
  3. 理想情况下,我只想更改一个高亮显示的高亮颜色时,我不想重新绘制整个CoreText UIView。
  4. 个别的Rects必须能够组合在一起形成一个高光,因为高光可以覆盖超出单个CTLine的范围。
  5. 最后,我想在CoreText视图上实现UIMenuController,所以我相信必须在CoreText UIView下面绘制亮点?
  6. 我非常感谢任何协助。

    这是我到目前为止所尝试的内容:

    我在UIScrollView中添加了一个新的UIView作为子视图来包含亮点。然后我为突出显示添加单独的UIViews。当我创建虚拟“测试”高亮显示时,它们在CoreText视图下正确显示。我也可以轻松改变高光颜色。但是,当我从CoreText视图获取有关线条的实际信息以创建真实高光时,它返回零线。这可能是因为drawRect还没有完成绘图还是别的什么?

    以下是一些问题:

    1. 我应该将UIView作为子视图添加到UIScrollView中,以包含所有高亮显示以及每个高亮显示的其他子视图吗?然后,将每个高亮显示UIView存储在嵌套的NSArray中?
    2. 如果我更改了CoreText UIView下方高光的颜色,是否会重新绘制整个UIView?如果是这样,我该如何设计呢?
    3. 如果我希望CoreText UIView接收UIMenuController的输入,我必须将高亮对象放在CoreText UIView下面吗?
    4. 我见过这个开源项目:

      https://github.com/Cocoanetics/NSAttributedString-Additions-for-HTML

      它的功能令人惊叹,但它比我需要的更加复杂和详细。代码使用CoreText呈现每个单独的字形,并允许突出显示,这很棒。但是,我试图保持我的代码简单和轻量级。我对CoreGraphics,CoreAnimation或CoreText知之甚少。

      我也看到了这个问题:

      Core Text - Get Pixel Coordinates from NSRange

      该问题的答案提供了确定给定行中给定范围的Rect的边界的调用。我想我可以搞清楚这些电话。

      更新

      这是我现在所做的。我确定这是错的,但这是我能找到的唯一方法让它发挥作用......

      1. 创建一个UIView作为容器,作为子视图添加到UIScrollView。
      2. 将UIView传递给CoreTextView。我还将一个字符串范围数组传递给CoreTextView,以指示应突出显示哪些字符串。
      3. 将CoreTextView添加为UIScrollView的子视图。这样,UIView就在CoreTextView下面。
      4. 在CoreTextView的drawRect:函数中,我绘制了CoreText。然后我调用一个函数来计算亮点。
      5. 根据行检查每个NSRange,并计算行中每个匹配的CGRect。
      6. CGRect用于创建新的UIView。 UIView被添加为UIView容器的子视图。它也被添加到NSArray中,以便我可以跟踪它并根据需要更改单个突出显示的突出显示颜色。
      7. 每次将高亮显示添加到容器或更改颜色时,都不会调用drawRect。但是,我知道这不是一个理想的解决方案。此解决方案存在问题:

        1. 当UIViewController在CoreTextView上调用setNeedsDisplay时,无法知道它何时完成绘制。我仍然找不到办法做回调告诉ViewController可以继续下一个任务。
        2. 通过将UIViews添加到另一个容器UIView中,drawRect在技术上是在自己的rect之外绘制项目,我知道这是不是。
        3. 我确信这不是最有效的方法,虽然看起来效果不错。

1 个答案:

答案 0 :(得分:2)

你的基本想法对于突出显示来说并不算太糟糕。我不知道它是否比在绘制时突出显示字形更容易,但这并不是一个坏主意,并且可以轻松打开和关闭突出显示,因此非常好。

不是创建大量视图,而是将核心文本放到CALayer上。然后,您可以创建其他突出显示图层以放置文本。这样可以将所有内容保留在单个视图中。图层比视图便宜得多。如果您只是尝试进行“荧光笔”样式突出显示,那么您只需为图层设置backgroundColorframe即可。