// AnimatingTextDrawing.h
#import <UIKit/UIKit.h>
@interface AnimatingTextDrawing : UIViewController
@end
#import "AnimatingTextDrawing.h"
#import <CoreText/CoreText.h>
#import <QuartzCore/QuartzCore.h>
@interface AnimatingTextDrawing()
@property (nonatomic, retain) CALayer *animationLayer;
@property (nonatomic, retain) CAShapeLayer *pathShapeLayer;
@end
@implementation AnimatingTextDrawing
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.animationLayer = [CALayer layer];
self.animationLayer.frame = self.view.frame;
[self.view.layer addSublayer:self.animationLayer];
[self setupPathShapeLayer];
[self drawAnimationPositiveDirection];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)setupPathShapeLayer
{
if (self.pathShapeLayer != nil) {
[self.pathShapeLayer removeFromSuperlayer];
[self.animationLayer removeFromSuperlayer];
self.pathShapeLayer = nil;
self.animationLayer = nil;
}
// Create path from text - See: http://www.codeproject.com/Articles/109729/Low-level-text-rendering
// Animating the drawing of a CGPath with CAShapeLayer - See: http://oleb.net/blog/2010/12/animating-drawing-of-cgpath-with-cashapelayer/
CGMutablePathRef letters = CGPathCreateMutable();
UIFont *font = [UIFont fontWithName:@"STHeitiTC-Medium" size:100.0];
NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"极" attributes:attrsDictionary];
// The CTLine opaque type represents a line of text. A CTLine object contains an array of glyph runs
// CTLineRef: A reference to a line object.
// CTLineCreateWithAttributedString(): reates a single immutable line object directly from an attributed string.
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
// CTLineGetGlyphRuns(): Returns the array of glyph runs that make up the line object.
CFArrayRef runArray = CTLineGetGlyphRuns(line);
// NSLog(@"CFArrayGetCount(runArray) is %ld",CFArrayGetCount(runArray));
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
// Get FONT for this run
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), NSFontAttributeName);
// NSLog(@"CTRunGetGlyphCount(run) is %ld", CTRunGetGlyphCount(run));
// for each GLYPH in run
for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
{
// get Glyph & Glyph-data
CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, thisGlyphRange, &glyph);
CTRunGetPositions(run, thisGlyphRange, &position);
// Get PATH of outline
{
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
CGPathAddPath(letters, &t, letter);
CGPathRelease(letter);
}
}
}
CFRelease(line);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointZero];
[path appendPath:[UIBezierPath bezierPathWithCGPath:letters]];
CGPathRelease(letters);
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = self.animationLayer.bounds;
pathLayer.bounds = CGPathGetBoundingBox(path.CGPath);
// pathLayer.backgroundColor = [[UIColor yellowColor] CGColor];
pathLayer.geometryFlipped = YES;
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [[UIColor blackColor] CGColor];
pathLayer.fillColor = nil;
pathLayer.lineWidth = 3.0f;
pathLayer.lineJoin = kCALineJoinBevel;
[self.animationLayer addSublayer:pathLayer];
self.pathShapeLayer = pathLayer;
}
// 正方向画字
- (void)drawAnimationPositiveDirection
{
[self.pathShapeLayer removeAllAnimations];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 2.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
// 设置速度函数
[pathAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
pathAnimation.delegate = self;
[self.pathShapeLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
}
// 反方向画字
- (void)drawAnimationReverseDirection
{
[self.pathShapeLayer removeAllAnimations];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
pathAnimation.duration = 2.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:1.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:0.3888f];
[self.pathShapeLayer addAnimation:pathAnimation forKey:@"strokeStart"];
}
// 反方向消失字
- (void)disappearAnimationPositiveDirection
{
[self.pathShapeLayer removeAllAnimations];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 2.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:1.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:0.0f];
[self.pathShapeLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
}
// 正方向消失字
- (void)disappearAnimationReverseDirection
{
[self.pathShapeLayer removeAllAnimations];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
pathAnimation.duration = 2.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.pathShapeLayer addAnimation:pathAnimation forKey:@"strokeStart"];
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animationDidStop");
[self.pathShapeLayer removeFromSuperlayer];
}
@end
Ref: Animating the drawing of a CGPath with CAShapeLayer