6

我猜这是用单个 CATextLayers 制作一个字符串,然后根据需要将它们沿曲线放置,然后进行动画处理。因为这就是我现在的工作,但它失去了字距。就是这样:

为什么我的弯曲文本不居中?

但是,与精益、卑鄙的 Core Animation 做事方式和尊重字距调整相比,Core Text 是否更高效,并且能够避免整个“绘制到上下文中”的废话,这会减慢一切?即避免 drawRect: 以及所有其他大大减慢速度的方面,例如以这种方式绘制到屏幕:

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

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

我希望有一种方法可以做到这一点,而不会丢失字距调整信息,并且仍然能够以 60fps 动态调整间距。

4

1 回答 1

12

当然,你可以这样做。不过,在 iOS 7 中,您无需一直深入到 Core Text。NSLayoutManager在很多情况下都可以处理。请参阅我为iOS:PTL编写的CurvyText演示。您可以拖动所有控制点并沿曲线查看文本布局。

要了解这种布局在纯 Core Text 和 Core Animation 中的速度有多快,请参阅 Rich Text 中的PinchText演示,Core Text。这个展示了如何调整 Core Text 布局以响应多点触控,因此文本似乎向您的手指弯曲。它包括如何使用 Core Animation 制作动画以获得平滑调整的示例(甚至当您移开手指时也会产生小的“飞溅”效果)。

我不太明白你所说的“整个画成一个让一切都慢下来的废话”是什么意思。我非常非常快地将它们绘制到上下文中(Core Animation 也做了很多绘制到上下文中)。

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

- (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:的。这些例程比一般的贝塞尔曲线更容易为圆编写,所以这可能是一个很好的起点。请注意,此代码并未特别优化。它的设计是为了可读性而不是速度,但它在 iPad 3 上仍然非常快。如果你需要它更快,有几种技术,包括可以完成的大量预先计算。pointForOffset:angleForOffset:

PinchText 演示是纯 Core Text 和 Core Animation 中的,并且相当复杂,因为它在 Accelerate 中完成所有数学运算(并且确实需要)。我怀疑您是否需要它,因为您的布局问题并不复杂。一些简单的 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:表示当该模型数据结构被修改时,该层需要重绘自身。它在每一帧都完全重新计算并将自己重新绘制到它的上下文中。如果做得正确,这可能会非常快。

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

于 2014-08-28T20:02:10.333 回答