建一个动画管理类
.h
#import <UIKit/UIKit.h> #import <Foundation/Foundation.h> @interface LLAnimationManager : NSObject /** * init */ - (instancetype)initWithAnimationDuration:(CGFloat)animationDuration; /** * the face layer move position */ - (CABasicAnimation *)moveAnimationWithFromPosition:(CGPoint)fromPosition toPosition:(CGPoint)toPosition; /** * the layer background color animation */ - (CABasicAnimation *)backgroundColorAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue; /** * the eye layer move position */ - (CABasicAnimation *)eyeMoveAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue; /** * mouth key frame animation */ - (CAKeyframeAnimation *)mouthKeyFrameAnimationWidthOffSet:(CGFloat)offSet on:(BOOL)on; /** * eyes close and open key frame animation */ - (CAKeyframeAnimation *)eyesCloseAndOpenAnimationWithRect:(CGRect)rect; @end
.m
#import "LLAnimationManager.h" #import <QuartzCore/QuartzCore.h> @interface LLAnimationManager() /** * the duration is the face moving time not include spring animation */ @property (nonatomic, assign) CGFloat animationDuration; @end @implementation LLAnimationManager /** * init */ - (instancetype)initWithAnimationDuration:(CGFloat)animationDuration { self = [super init]; if (self) { _animationDuration = animationDuration; } return self; } /** * faceLayer move animation */ - (CABasicAnimation *)moveAnimationWithFromPosition:(CGPoint)fromPosition toPosition:(CGPoint)toPosition { CABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; moveAnimation.fromValue = [NSValue valueWithCGPoint:fromPosition]; moveAnimation.toValue = [NSValue valueWithCGPoint:toPosition]; moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; moveAnimation.duration = _animationDuration * 2 /3; moveAnimation.removedOnCompletion = NO; moveAnimation.fillMode = kCAFillModeForwards; return moveAnimation; } /** * layer background color animation */ - (CABasicAnimation *)backgroundColorAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue { CABasicAnimation *colorAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; colorAnimation.fromValue = fromValue; colorAnimation.toValue = toValue; colorAnimation.duration = _animationDuration * 2 /3; colorAnimation.removedOnCompletion = NO; colorAnimation.fillMode = kCAFillModeForwards; return colorAnimation; } /** * the eyes layer move */ - (CABasicAnimation *)eyeMoveAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue{ CABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; moveAnimation.fromValue = fromValue; moveAnimation.toValue = toValue; moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; moveAnimation.duration = _animationDuration / 3; moveAnimation.removedOnCompletion = NO; moveAnimation.fillMode = kCAFillModeForwards; return moveAnimation; } /** * mouth key frame animation */ - (CAKeyframeAnimation *)mouthKeyFrameAnimationWidthOffSet:(CGFloat)offSet on:(BOOL)on{ CGFloat frameNumber = _animationDuration * 60 / 3; CGFloat frameValue = on ? offSet : 0; NSMutableArray *arrayFrame = [NSMutableArray array]; for (int i = 0; i < frameNumber; i++) { if (on) { frameValue = frameValue - offSet / frameNumber; } else { frameValue = frameValue + offSet / frameNumber; } [arrayFrame addObject:@(frameValue)]; } CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"mouthOffSet"]; keyAnimation.values = arrayFrame; keyAnimation.duration = _animationDuration / 4; if (!on && _animationDuration >= 1.f) { keyAnimation.beginTime = CACurrentMediaTime() + _animationDuration / 12; } keyAnimation.removedOnCompletion = NO; keyAnimation.fillMode = kCAFillModeForwards; return keyAnimation; } /** * eyes close and open key frame animation */ - (CAKeyframeAnimation *)eyesCloseAndOpenAnimationWithRect:(CGRect)rect { CGFloat frameNumber = _animationDuration * 180 / 9; // 180 frame erver second CGFloat eyesX = rect.origin.x; CGFloat eyesY = rect.origin.y; CGFloat eyesWidth = rect.size.width; CGFloat eyesHeight = rect.size.height; NSMutableArray *arrayFrame = [NSMutableArray array]; for (int i = 0; i < frameNumber; i++) { if (i < frameNumber / 3) { // close eyesHeight = eyesHeight - rect.size.height / (frameNumber / 3); } else if (i >= frameNumber / 3 && i < frameNumber * 2 / 3) { // zero eyesHeight = 0; } else { // open eyesHeight = eyesHeight + rect.size.height / (frameNumber / 3); } eyesY = (rect.size.height - eyesHeight) / 2; [arrayFrame addObject:[NSValue valueWithCGRect:CGRectMake(eyesX, eyesY, eyesWidth, eyesHeight)]]; } CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"eyeRect"]; keyAnimation.values = arrayFrame; keyAnimation.duration = _animationDuration / 3; keyAnimation.removedOnCompletion = NO; keyAnimation.fillMode = kCAFillModeForwards; return keyAnimation; } @end
创建一个类:继承CALayer
.h
#import <QuartzCore/QuartzCore.h> #import <UIKit/UIKit.h> @interface LLEyesLayer : CALayer /** * eye property */ @property (nonatomic, assign) CGRect eyeRect; @property (nonatomic, assign) CGFloat eyeDistance; @property (nonatomic, strong) UIColor *eyeColor; @property (nonatomic, assign) BOOL isLiking; @property (nonatomic, assign) CGFloat mouthOffSet; @property (nonatomic, assign) CGFloat mouthY; @end
.m
#import "LLEyesLayer.h" @implementation LLEyesLayer /** * init layer * * @return self */ - (instancetype)init { if (self = [super init]) { // 默认属性 _eyeRect = CGRectMake(0, 0, 0, 0); _mouthOffSet = 0.f; } return self; } - (instancetype)initWithLayer:(LLEyesLayer *)layer { self = [super initWithLayer:layer]; if (self) { self.eyeRect = layer.eyeRect; self.eyeDistance = layer.eyeDistance; self.eyeColor = layer.eyeColor; self.isLiking = layer.isLiking; self.mouthOffSet = layer.mouthOffSet; self.mouthY = layer.mouthY; } return self; } /** * draw */ - (void)drawInContext:(CGContextRef)ctx { UIBezierPath *bezierLeft = [UIBezierPath bezierPathWithOvalInRect:_eyeRect]; UIBezierPath *bezierRight = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_eyeDistance, _eyeRect.origin.y, _eyeRect.size.width, _eyeRect.size.height)]; UIBezierPath *bezierMouth = [UIBezierPath bezierPath]; CGFloat mouthWidth = _eyeRect.size.width + _eyeDistance; if (_isLiking) { // funny mouth [bezierMouth moveToPoint:CGPointMake(0, _mouthY)]; [bezierMouth addCurveToPoint:CGPointMake(mouthWidth, _mouthY) controlPoint1:CGPointMake(mouthWidth - _mouthOffSet * 3 / 4, _mouthY + _mouthOffSet / 2) controlPoint2:CGPointMake(mouthWidth - _mouthOffSet / 4, _mouthY + _mouthOffSet / 2)]; } else { // boring mouth bezierMouth = [UIBezierPath bezierPathWithRect:CGRectMake(0, _mouthY, mouthWidth, _eyeRect.size.height / 4)]; } [bezierMouth closePath]; CGContextAddPath(ctx, bezierLeft.CGPath); CGContextAddPath(ctx, bezierRight.CGPath); CGContextAddPath(ctx, bezierMouth.CGPath); CGContextSetFillColorWithColor(ctx, _eyeColor.CGColor); CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); CGContextFillPath(ctx); } /** * key animation */ +(BOOL)needsDisplayForKey:(NSString *)key{ if ([key isEqual:@"mouthOffSet"]) { return YES; } if ([key isEqual:@"eyeRect"]) { return YES; } return [super needsDisplayForKey:key]; } @end
创建一个开关,继承:UIView
.h
#import <UIKit/UIKit.h> @protocol LLSwitchDelegate; IB_DESIGNABLE @interface LLSwitch : UIView /** * switch on color */ @property (nonatomic, strong) IBInspectable UIColor *onColor; /** * switch off color */ @property (nonatomic, strong) IBInspectable UIColor *offColor; /** * face on and off color */ @property (nonatomic, strong) IBInspectable UIColor *faceColor; /** * the duration is the face moving time */ @property (nonatomic, assign) IBInspectable CGFloat animationDuration; /** * the switch status is or isn't on */ @property (nonatomic, assign) IBInspectable BOOL on; @property (nonatomic, weak) IBOutlet id <LLSwitchDelegate> delegate; @end #pragma mark LLSwitch delegate @protocol LLSwitchDelegate <NSObject> @optional - (void)didTapLLSwitch:(LLSwitch *)llSwitch; - (void)animationDidStopForLLSwitch:(LLSwitch *)llSwitch; @end
.m
#import "LLSwitch.h" #import "LLAnimationManager.h" #import "LLEyesLayer.h" NSString * const FaceMoveAnimationKey = @"FaceMoveAnimationKey"; NSString * const BackgroundColorAnimationKey = @"BackgroundColorAnimationKey"; NSString * const EyesMoveStartAnimationKey = @"EyesMoveStartAnimationKey"; NSString * const EyesMoveEndAnimationKey = @"EyesMoveEndAnimationKey"; NSString * const EyesMoveBackAnimationKey = @"EyesMoveBackAnimationKey"; NSString * const MouthFrameAnimationKey = @"MouthFrameAnimationKey"; NSString * const EyesCloseAndOpenAnimationKey = @"EyesCloseAndOpenAnimationKey"; @interface LLSwitch() /** * switch background view */ @property (nonatomic, strong) UIView *backgroundView; /** * face layer */ @property (nonatomic, strong) CAShapeLayer *circleFaceLayer; /** * paddingWidth */ @property (nonatomic, assign) CGFloat paddingWidth; /** * eyes layer */ @property (nonatomic, strong) LLEyesLayer *eyesLayer; /** * face radius */ @property (nonatomic, assign) CGFloat circleFaceRadius; /** * the faceLayer move distance */ @property (nonatomic, assign) CGFloat moveDistance; /** * handler layer animation manager */ @property (nonatomic, strong) LLAnimationManager *animationManager; /** * whether is animated */ @property (nonatomic, assign) BOOL isAnimating; @property (nonatomic, assign) CGFloat faceLayerWidth; @end @implementation LLSwitch - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self initSetUpView]; } return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self initSetUpView]; } return self; } - (void)initSetUpView { /** * check the switch width and height */ NSAssert(self.frame.size.width >= self.frame.size.height, @"switch width must be tall!"); /** * init property */ _onColor = [UIColor colorWithRed:73/255.0 green:182/255.0 blue:235/255.0 alpha:1.f]; _offColor = [UIColor colorWithRed:211/255.0 green:207/255.0 blue:207/255.0 alpha:1.f]; _faceColor = [UIColor whiteColor]; _paddingWidth = self.frame.size.height * 0.1; _circleFaceRadius = (self.frame.size.height - 2 * _paddingWidth) / 2; _animationDuration = 1.2f; _animationManager = [[LLAnimationManager alloc] initWithAnimationDuration:_animationDuration]; _moveDistance = self.frame.size.width - _paddingWidth * 2 - _circleFaceRadius * 2; _on = NO; _isAnimating = NO; /** * setting init property */ self.backgroundView.backgroundColor = _offColor; self.circleFaceLayer.fillColor = _faceColor.CGColor; self.faceLayerWidth = self.circleFaceLayer.frame.size.width; [self.eyesLayer setNeedsDisplay]; [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapSwitch)]]; } #pragma mark set property - (void)setBackgroundColor:(UIColor *)backgroundColor { return; } - (void)setOffColor:(UIColor *)offColor { _offColor = offColor; if (!_on) { _backgroundView.backgroundColor = offColor; _eyesLayer.eyeColor = offColor; [self.eyesLayer setNeedsDisplay]; } } - (void)setOnColor:(UIColor *)onColor { _onColor = onColor; if (_on) { _backgroundView.backgroundColor = onColor; _eyesLayer.eyeColor = onColor; [self.eyesLayer setNeedsDisplay]; } } - (void)setFaceColor:(UIColor *)faceColor { _faceColor = faceColor; _circleFaceLayer.fillColor = faceColor.CGColor; } - (void)setAnimationDuration:(CGFloat)animationDuration { _animationDuration = animationDuration; _animationManager = [[LLAnimationManager alloc] initWithAnimationDuration:_animationDuration]; } - (void)setOn:(BOOL)on { _on = on; if (on) { self.backgroundView.backgroundColor = _onColor; self.circleFaceLayer.position = CGPointMake(self.circleFaceLayer.position.x + _moveDistance, self.circleFaceLayer.position.y); self.eyesLayer.eyeColor = _onColor; self.eyesLayer.isLiking = YES; self.eyesLayer.mouthOffSet = _eyesLayer.frame.size.width; [self.eyesLayer needsDisplay]; } } #pragma mark GestureRecognizer - (void)handleTapSwitch { if (_isAnimating) { return; } _isAnimating = YES; // faceLayer CABasicAnimation *moveAnimation = [_animationManager moveAnimationWithFromPosition:_circleFaceLayer.position toPosition:_on ? CGPointMake(_circleFaceLayer.position.x - _moveDistance, _circleFaceLayer.position.y) : CGPointMake(_circleFaceLayer.position.x + _moveDistance, _circleFaceLayer.position.y)]; moveAnimation.delegate = self; [_circleFaceLayer addAnimation:moveAnimation forKey:FaceMoveAnimationKey]; // backfroundView CABasicAnimation *colorAnimation = [_animationManager backgroundColorAnimationFromValue:(id)(_on ? _onColor : _offColor).CGColor toValue:(id)(_on ? _offColor : _onColor).CGColor]; [_backgroundView.layer addAnimation:colorAnimation forKey:BackgroundColorAnimationKey]; // eyesLayer CABasicAnimation *rotationAnimation = [_animationManager eyeMoveAnimationFromValue:@(0) toValue:@(_on ? -_faceLayerWidth : _faceLayerWidth)]; rotationAnimation.delegate = self; [_eyesLayer addAnimation:rotationAnimation forKey:EyesMoveStartAnimationKey]; _circleFaceLayer.masksToBounds = YES; if (_on) { [self eyesKeyFrameAnimationStart]; } // start delegate if ([self.delegate respondsToSelector:@selector(didTapLLSwitch:)]) { [self.delegate didTapLLSwitch:self]; } } #pragma mark Init /** * init backgroundView * * @return backgroundView */ - (UIView *)backgroundView { if (!_backgroundView) { _backgroundView = [[UIView alloc] init]; _backgroundView.frame = self.bounds; _backgroundView.layer.cornerRadius = self.frame.size.height / 2; _backgroundView.layer.masksToBounds = YES; [self addSubview:_backgroundView]; } return _backgroundView; } /** * init circleFaceLayer * * @return circleFaceLayer */ - (CAShapeLayer *)circleFaceLayer { if (!_circleFaceLayer) { _circleFaceLayer = [CAShapeLayer layer]; [_circleFaceLayer setFrame:CGRectMake(_paddingWidth, _paddingWidth, _circleFaceRadius * 2, _circleFaceRadius *2)]; UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:_circleFaceLayer.bounds]; _circleFaceLayer.path = circlePath.CGPath; [self.backgroundView.layer addSublayer:_circleFaceLayer]; } return _circleFaceLayer; } /** * eyes and mouth layer * * @return eyesLayer */ - (LLEyesLayer *)eyesLayer { if (!_eyesLayer) { _eyesLayer = [LLEyesLayer layer]; _eyesLayer.eyeRect = CGRectMake(0, 0, _faceLayerWidth / 6, _circleFaceLayer.frame.size.height * 0.22); _eyesLayer.eyeDistance = _faceLayerWidth / 3; _eyesLayer.eyeColor = _offColor; _eyesLayer.isLiking = NO; _eyesLayer.mouthY = _eyesLayer.eyeRect.size.height * 7 / 4; _eyesLayer.frame = CGRectMake(_faceLayerWidth / 4, _circleFaceLayer.frame.size.height * 0.28, _faceLayerWidth / 2, _circleFaceLayer.frame.size.height * 0.72); // _eyesLayer.backgroundColor = [UIColor redColor].CGColor; [self.circleFaceLayer addSublayer:_eyesLayer]; } return _eyesLayer; } #pragma mark AnimationDelegate - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if (flag) { // start eyes ending animation if (anim == [_eyesLayer animationForKey:EyesMoveStartAnimationKey]) { _eyesLayer.eyeColor = _on ? _offColor: _onColor; _eyesLayer.isLiking = !_on; [_eyesLayer setNeedsDisplay]; CABasicAnimation *rotationAnimation = [_animationManager eyeMoveAnimationFromValue:@(_on ? _faceLayerWidth : -_faceLayerWidth) toValue:@(_on ? -_faceLayerWidth / 6 : _faceLayerWidth / 6)]; rotationAnimation.delegate = self; [_eyesLayer addAnimation:rotationAnimation forKey:EyesMoveEndAnimationKey]; if (!_on) { [self eyesKeyFrameAnimationStart]; } } // start eyes back animation if (anim == [_eyesLayer animationForKey:EyesMoveEndAnimationKey]) { CABasicAnimation *rotationAnimation = [_animationManager eyeMoveAnimationFromValue:@(_on ? -_faceLayerWidth / 6 : _faceLayerWidth / 6) toValue:@(0)]; rotationAnimation.delegate = self; [_eyesLayer addAnimation:rotationAnimation forKey:EyesMoveBackAnimationKey]; if (!_on) { CAKeyframeAnimation *eyesKeyFrameAnimation = [_animationManager eyesCloseAndOpenAnimationWithRect:_eyesLayer.eyeRect]; [_eyesLayer addAnimation:eyesKeyFrameAnimation forKey:EyesCloseAndOpenAnimationKey]; } } // eyes back animation end if (anim == [_eyesLayer animationForKey:EyesMoveBackAnimationKey]) { [_eyesLayer removeAllAnimations]; _eyesLayer.mouthOffSet = _on ? 0 : _eyesLayer.frame.size.width; if (_on) { _circleFaceLayer.position = CGPointMake(_circleFaceLayer.position.x - _moveDistance, _circleFaceLayer.position.y); _on = NO; } else { _circleFaceLayer.position = CGPointMake(_circleFaceLayer.position.x + _moveDistance, _circleFaceLayer.position.y); _on = YES; } _isAnimating = NO; // stop delegate if ([self.delegate respondsToSelector:@selector(animationDidStopForLLSwitch:)]) { [self.delegate animationDidStopForLLSwitch:self]; } } } } /** * add mouth keyFrameAnimation */ - (void)eyesKeyFrameAnimationStart { CAKeyframeAnimation *keyAnimation = [_animationManager mouthKeyFrameAnimationWidthOffSet:_eyesLayer.frame.size.width on:_on]; [_eyesLayer addAnimation:keyAnimation forKey:MouthFrameAnimationKey]; } - (void)dealloc { self.delegate = nil; } @end
在需要的地方使用即可:
#import "ViewController.h" #import "LLSwitch.h" @interface ViewController () <LLSwitchDelegate> @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LLSwitch *llSwitch = [[LLSwitch alloc] initWithFrame:CGRectMake(100, 100, 120, 60)]; [self.view addSubview:llSwitch]; llSwitch.delegate = self; } -(void)didTapLLSwitch:(LLSwitch *)llSwitch { NSLog(@"start"); } - (void)animationDidStopForLLSwitch:(LLSwitch *)llSwitch { NSLog(@"stop"); } @end