AnimatedPathViewController.h
#import <UIKit/UIKit.h> #import <QuartzCore/QuartzCore.h> @interface AnimatedPathViewController : UIViewController { CALayer *_animationLayer; CAShapeLayer *_pathLayer; CALayer *_penLayer; } @property (nonatomic, retain) CALayer *animationLayer; @property (nonatomic, retain) CAShapeLayer *pathLayer; @property (nonatomic, retain) CALayer *penLayer; - (IBAction) replayButtonTapped:(id)sender; - (IBAction) drawingTypeSelectorTapped:(id)sender; @end
AnimatedPathViewController.m
#import <QuartzCore/QuartzCore.h> #import <CoreText/CoreText.h> #import "AnimatedPathViewController.h" @implementation AnimatedPathViewController @synthesize animationLayer = _animationLayer; @synthesize pathLayer = _pathLayer; @synthesize penLayer = _penLayer; - (void) setupDrawingLayer { if (self.pathLayer != nil) { [self.penLayer removeFromSuperlayer]; [self.pathLayer removeFromSuperlayer]; self.pathLayer = nil; self.penLayer = nil; } CGRect pathRect = CGRectInset(self.animationLayer.bounds, 100.0f, 100.0f); CGPoint bottomLeft = CGPointMake(CGRectGetMinX(pathRect), CGRectGetMinY(pathRect)); CGPoint topLeft = CGPointMake(CGRectGetMinX(pathRect), CGRectGetMinY(pathRect) + CGRectGetHeight(pathRect) * 2.0f / 3.0f); CGPoint bottomRight = CGPointMake(CGRectGetMaxX(pathRect), CGRectGetMinY(pathRect)); CGPoint topRight = CGPointMake(CGRectGetMaxX(pathRect), CGRectGetMinY(pathRect) + CGRectGetHeight(pathRect) * 2.0f / 3.0f); CGPoint roofTip = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMaxY(pathRect)); UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:bottomLeft]; [path addLineToPoint:topLeft]; [path addLineToPoint:roofTip]; [path addLineToPoint:topRight]; [path addLineToPoint:topLeft]; [path addLineToPoint:bottomRight]; [path addLineToPoint:topRight]; [path addLineToPoint:bottomLeft]; [path addLineToPoint:bottomRight]; CAShapeLayer *pathLayer = [CAShapeLayer layer]; pathLayer.frame = self.animationLayer.bounds; pathLayer.bounds = pathRect; pathLayer.geometryFlipped = YES; pathLayer.path = path.CGPath; pathLayer.strokeColor = [[UIColor blackColor] CGColor]; pathLayer.fillColor = nil; pathLayer.lineWidth = 10.0f; pathLayer.lineJoin = kCALineJoinBevel; [self.animationLayer addSublayer:pathLayer]; self.pathLayer = pathLayer; UIImage *penImage = [UIImage imageNamed:@"pen.png"]; CALayer *penLayer = [CALayer layer]; penLayer.contents = (id)penImage.CGImage; penLayer.anchorPoint = CGPointZero; penLayer.frame = CGRectMake(0.0f, 0.0f, penImage.size.width, penImage.size.height); [pathLayer addSublayer:penLayer]; self.penLayer = penLayer; } - (void) setupTextLayer { if (self.pathLayer != nil) { [self.penLayer removeFromSuperlayer]; [self.pathLayer removeFromSuperlayer]; self.pathLayer = nil; self.penLayer = nil; } CGMutablePathRef letters = CGPathCreateMutable(); CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica-Bold"), 72.0f, NULL); NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys: (id)font, kCTFontAttributeName, nil]; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"高海峰" attributes:attrs]; CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString); CFArrayRef runArray = CTLineGetGlyphRuns(line); for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++) { CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex); CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) { CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1); CGGlyph glyph; CGPoint position; CTRunGetGlyphs(run, thisGlyphRange, &glyph); CTRunGetPositions(run, thisGlyphRange, &position); { 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); CFRelease(font); CAShapeLayer *pathLayer = [CAShapeLayer layer]; pathLayer.frame = self.animationLayer.bounds; pathLayer.bounds = CGPathGetBoundingBox(path.CGPath); 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.pathLayer = pathLayer; UIImage *penImage = [UIImage imageNamed:@"pen.png"]; CALayer *penLayer = [CALayer layer]; penLayer.contents = (id)penImage.CGImage; penLayer.anchorPoint = CGPointZero; penLayer.frame = CGRectMake(0.0f, 0.0f, penImage.size.width, penImage.size.height); [pathLayer addSublayer:penLayer]; self.penLayer = penLayer; } - (void) startAnimation { [self.pathLayer removeAllAnimations]; [self.penLayer removeAllAnimations]; self.penLayer.hidden = NO; CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; pathAnimation.duration = 10.0; pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f]; pathAnimation.toValue = [NSNumber numberWithFloat:1.0f]; [self.pathLayer addAnimation:pathAnimation forKey:@"strokeEnd"]; CAKeyframeAnimation *penAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; penAnimation.duration = 10.0; penAnimation.path = self.pathLayer.path; penAnimation.calculationMode = kCAAnimationPaced; penAnimation.delegate = self; [self.penLayer addAnimation:penAnimation forKey:@"position"]; } - (void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { self.penLayer.hidden = YES; } - (void)viewDidLoad { [super viewDidLoad]; self.animationLayer = [CALayer layer]; self.animationLayer.frame = CGRectMake(20.0f, 64.0f, CGRectGetWidth(self.view.layer.bounds) - 40.0f, CGRectGetHeight(self.view.layer.bounds) - 84.0f); [self.view.layer addSublayer:self.animationLayer]; [self setupDrawingLayer]; [self startAnimation]; } - (void)dealloc { self.animationLayer = nil; self.pathLayer = nil; self.penLayer = nil; [super dealloc]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return UIInterfaceOrientationIsPortrait(interfaceOrientation); } - (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { self.animationLayer.frame = CGRectMake(20.0f, 64.0f, CGRectGetWidth(self.view.layer.bounds) - 40.0f, CGRectGetHeight(self.view.layer.bounds) - 84.0f); self.pathLayer.frame = self.animationLayer.bounds; self.penLayer.frame = self.penLayer.bounds; } - (IBAction) replayButtonTapped:(id)sender { [self startAnimation]; } - (IBAction) drawingTypeSelectorTapped:(id)sender { UISegmentedControl *drawingTypeSelector = (UISegmentedControl *)sender; switch (drawingTypeSelector.selectedSegmentIndex) { case 0: [self setupDrawingLayer]; [self startAnimation]; break; case 1: [self setupTextLayer]; [self startAnimation]; break; } } @end
效果图: