以最高效的方式在曲线上绘制文本并为其制作动画?

时间:2014-08-28 19:08:24

标签: ios core-animation core-text kerning

我猜这是用单个CATextLayers制作一个字符串,然后根据需要在曲线上定位它们,然后制作动画。因为那就是我现在的工作,但它失去了科宁。方法如下:

Why isn't my curved text centering itself?

但是核心文本是否更具高效性并且能够避免整个“吸入上下文”的废话,与精简,平均核心动画的做事方式相比,减慢一切,并尊重字距?即避免使用drawRect:以及所有其他方面会大大减慢速度,例如以这种方式绘制到屏幕上:

https://github.com/darcyliu/CocoaSampleCode/tree/master/CoreTextArcCocoa

想象一个由200个字符组成的字符串,围绕一个圆圈弯曲,能够为字符之间的间距设置动画,希望稳定在60fps。这可以通过Core Animation实现,但这可以通过将字符串分解为单个字符并将它们以相等的间距放在圆圈周围,这会导致完全丢失字距调整信息。

我希望有一种方法可以在不丢失字距调整信息的情况下完成此操作,并且仍能够以60fps动态调整间距。

1 个答案:

答案 0 :(得分:12)

当然,你可以做到。但是,使用iOS 7,您无需一直向下到Core Text。 NSLayoutManager可以在很多情况下处理它。请参阅我为CurvyText撰写的iOS:PTL演示。您可以拖动所有控制点并沿曲线查看文本布局。

要了解此布局在纯Core Text和Core Animation中的速度有多快,请参阅PinchTextRich Text, Core Text演示。这一部分展示了如何调整Core Text布局以响应多点触控,因此文本似乎向您的手指弯曲。它包含了如何使用Core Animation进行动画处理以获得平滑调整的示例(当您移开手指时甚至会产生一些小的“泼溅”效果)。

我不太明白你的意思是“整个画面变成了一个让所有事情变慢的背景废话。”我非常非常快速地将这些绘制到一个上下文中(并且Core Animation也在很多情况下绘制了上下文)。

围绕圆圈弯曲文字比这些演示更容易。诀窍是计算沿着圆圈的点,并在要求布局管理器绘制字形之前使用这些点来平移和旋转上下文。这是CurvyTextView中的一个示例drawText(沿着Bézier曲线绘制)。

- (void)drawText {
  if ([self.attributedString length] == 0) { return; }

  NSLayoutManager *layoutManager = self.layoutManager;

  CGContextRef context = UIGraphicsGetCurrentContext();
  NSRange glyphRange;
  CGRect lineRect = [layoutManager lineFragmentRectForGlyphAtIndex:0
                                                    effectiveRange:&glyphRange];

  double offset = 0;
  CGPoint lastGlyphPoint = self.P0;
  CGFloat lastX = 0;
  for (NSUInteger glyphIndex = glyphRange.location;
       glyphIndex < NSMaxRange(glyphRange);
       ++glyphIndex) {
    CGContextSaveGState(context);

    CGPoint location = [layoutManager locationForGlyphAtIndex:glyphIndex];

    CGFloat distance = location.x - lastX;  // Assume single line
    offset = [self offsetAtDistance:distance
                          fromPoint:lastGlyphPoint
                          andOffset:offset];
    CGPoint glyphPoint = [self pointForOffset:offset];
    double angle = [self angleForOffset:offset];

    lastGlyphPoint = glyphPoint;
    lastX = location.x;

    CGContextTranslateCTM(context, glyphPoint.x, glyphPoint.y);
    CGContextRotateCTM(context, angle);

    [layoutManager drawGlyphsForGlyphRange:NSMakeRange(glyphIndex, 1)
                                   atPoint:CGPointMake(-(lineRect.origin.x + location.x),
                                                       -(lineRect.origin.y + location.y))];

    CGContextRestoreGState(context);
  }
}

这种“魔力”在于计算所需的变换,这在offsetAtDistance:fromPoint:andOffset:pointForOffset:angleForOffset:中完成。这些例程比圆形Bézier曲线更简单,因此这可能是一个非常好的起点。请注意,此代码未经过特别优化。它的设计目的是为了提高速度,但在iPad 3上仍然非常快。如果你需要它更快,there are several techniques,包括可以完成的大量预先计算。

PinchText演示采用纯粹的核心文本和核心动画,并且由于它在加速(并且确实需要)中完成所有数学运算,因此相当复杂。我怀疑你需要它,因为你的布局问题并不复杂。一些直截了当的C可以在很长一段时间内计算出你需要的一切。但PinchText演示确实展示了如何让Core Animation更精美地管理转换。看看addTouches:inView:scale:

- (void)addTouches:(NSSet *)touches inView:(UIView *)view scale:(CGFloat)scale
{
  for (UITouch *touch in touches) {
    TouchPoint *touchPoint = [TouchPoint touchPointForTouch:touch inView:view scale:scale];
    NSString *keyPath = [self touchPointScaleKeyPathForTouchPoint:touchPoint];

    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:keyPath];
    anim.duration = kStartTouchAnimationDuration;
    anim.fromValue = @0;
    anim.toValue = @(touchPoint.scale);
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [self addAnimation:anim forKey:keyPath];

    [self.touchPointForIdentifier setObject:touchPoint forKey:touchPoint.identifier];
  }
}

这里发生的是它正在动画模型数据(这里的“比例”是“这种触摸对布局有多大影响;”它与变换无关)。 needsDisplayForKey:表示修改该模型数据结构时,该层需要重绘自身。它完全重新计算并重新绘制每一帧的上下文。做得对,这可以非常快。

此代码应该有助于您入门。不要过度推动这本书,但在iOS Pushing the Limits第21章中广泛讨论了CurvyText演示。