原创Blog,转载请注明出处
http://blog.csdn.net/hello_hwc?viewmode=list
我的stackoverflow
我的Github
https://github.com/LeoMobileDeveloper
前言:当我们去修改一个CALayer的属性的时候,通常是会自动创建渐变的动画。但是,很多时候,默认的属性并不能满足我们的要求,我们希望自定义的属性也有动画。这就是本文要讲解的内容
本文中,就是感兴趣的扫描区域
@property (nonatomic)CGRect focusRect;
设置为@dynamic ,这样做是为了让CoreAnimation自动帮我门计算,在切换动画的时候每一帧对应focusRect的值
@implementation LHBackgroundLayer
@dynamic focusRect;
//****
@end
在这个例子中,四个边角,包括绘制明暗相间的区域,都是调用CoreGraphics的代码绘制的
-(void)drawInContext:(CGContextRef)ctx{
UIBezierPath * focusPath = [self createBezierPathWithBounds:self.bounds focusRect:self.focusRect];
CGContextAddPath(ctx, focusPath.CGPath);
CGContextSetFillColorWithColor(ctx, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5].CGColor);
CGContextEOFillPath(ctx);
CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);
CGContextSetLineWidth(ctx,2.0);
CGContextMoveToPoint(ctx, self.focusRect.origin.x + CORNERLENGTH, self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x,self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x, self.focusRect.origin.y + CORNERLENGTH);
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width - CORNERLENGTH, self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width, self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width, self.focusRect.origin.y + CORNERLENGTH);
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width, self.focusRect.origin.y + self.focusRect.size.height - CORNERLENGTH);
CGContextAddLineToPoint(ctx,self.focusRect.origin.x + self.focusRect.size.width,self.focusRect.origin.y + self.focusRect.size.height);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width - CORNERLENGTH, self.focusRect.origin.y + self.focusRect.size.height);
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, self.focusRect.origin.x, self.focusRect.origin.y - CORNERLENGTH + self.focusRect.size.height);
CGContextAddLineToPoint(ctx,self.focusRect.origin.x,self.focusRect.origin.y+self.focusRect.size.height);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + CORNERLENGTH, self.focusRect.origin.y + self.focusRect.size.height);
CGContextStrokePath(ctx);
UIBezierPath * beziper = [UIBezierPath bezierPathWithRect:self.focusRect];
CGContextAddPath(ctx, beziper.CGPath);
CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(ctx, 0.5);
CGContextStrokePath(ctx);
}
-(UIBezierPath *)createBezierPathWithBounds:(CGRect)bounds focusRect:(CGRect)focusRect{
UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:bounds];
UIBezierPath * focusPath = [UIBezierPath bezierPathWithRect:focusRect];
[bezierPath appendPath:focusPath];
return bezierPath;
}
小的tips
明暗相间的效果是通过 CGContextEOFillPath(ctx);奇偶fill的方式来绘制的
+(BOOL)needsDisplayForKey:(NSString *)key{
if ([key isEqualToString:@"focusRect"]) {
return true;
}
return [super needsDisplayForKey:key];
}
-(id)actionForKey:(NSString *)event{
if ([event isEqualToString:@"focusRect"]) {
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:event];
anim.fromValue = [[self presentationLayer] valueForKey:event];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
anim.delegate = self;
anim.duration = self.animateDurationWhenFocusChange;
[anim setValue:@"leoStateChageAnimation" forKey:@"lhCodeKey"];
return anim;
}
return [super actionForKey:event];
}
小的tips
关于Layer有三个model tree,其中一个是CoreAnimation私有的,一般无需考虑。还有两个,其中一个是presentationLayer,一个是实际的model layer,其中通过presentationLayer可以获得动画的过程中每一帧layer的属性值
到这里,为calayer添加一个可动画的属性部分就完成了
头文件
//
// LHBackgroundLayer.h
// LeoCodeScanner
//
// Created by huangwenchen on 16/1/11.
// Copyright © 2016年 LeoHuang. All rights reserved.
//
#import
#import
@interface LHBackgroundLayer : CALayer
-(instancetype)initWithBounds:(CGRect)bounds BackgroundColor:(UIColor *)backgroundColor focusRect:(CGRect)focusRect;
-(void)updateFocus:(CGRect)focusRect Completion:(void(^)(void))completion;
@property (assign,nonatomic)CGFloat animateDurationWhenFocusChange;
@end
.m文件
//
// LHBackgroundLayer.m
// LeoCodeScanner
//
// Created by huangwenchen on 16/1/11.
// Copyright © 2016年 LeoHuang. All rights reserved.
//
#import "LHBackgroundLayer.h"
#import "CALayer+LHHelper.h"
#import "LHGradientLineLayer.h"
#define CORNERLENGTH 15
@interface LHBackgroundLayer()
@property (strong,nonatomic)LHGradientLineLayer * lineLayer;
@property (nonatomic)CGRect focusRect;
@property (copy,nonatomic)void(^completion)(void);
@end
@implementation LHBackgroundLayer
@dynamic focusRect;
-(instancetype)initWithBounds:(CGRect)bounds BackgroundColor:(UIColor *)backgroundColor focusRect:(CGRect)focusRect{
if (self = [super init]) {
self.anchorPoint = CGPointZero;
self.position = CGPointZero;
self.bounds = bounds;
self.animateDurationWhenFocusChange = 0.4;
self.contentsScale = [UIScreen mainScreen].scale;
self.needsDisplayOnBoundsChange = true;
self.focusRect = focusRect;
self.lineLayer = [[LHGradientLineLayer alloc] initWithColor:[UIColor greenColor] Frame:CGRectMake(focusRect.origin.x, focusRect.origin.y, focusRect.size.width, 1)];
[self addSublayer:self.lineLayer];
[self setNeedsDisplay];
[self startAnimate];
}
return self;
}
+(BOOL)needsDisplayForKey:(NSString *)key{
if ([key isEqualToString:@"focusRect"]) {
return true;
}
return [super needsDisplayForKey:key];
}
-(id)actionForKey:(NSString *)event{
if ([event isEqualToString:@"focusRect"]) {
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:event];
anim.fromValue = [[self presentationLayer] valueForKey:event];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
anim.delegate = self;
anim.duration = self.animateDurationWhenFocusChange;
[anim setValue:@"leoStateChageAnimation" forKey:@"lhCodeKey"];
return anim;
}
return [super actionForKey:event];
}
-(void)animationDidStart:(CAAnimation *)anim{
if ([[anim valueForKey:@"lhCodeKey"] isEqualToString:@"leoStateChageAnimation"]) {
[self stopAnimate];
}
}
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if ([[anim valueForKey:@"lhCodeKey"] isEqualToString:@"leoStateChageAnimation"]) {
[self startAnimate];
if (self.completion) {
self.completion();
self.completion = nil;
}
}
}
-(void)updateFocus:(CGRect)focusRect Completion:(void (^)(void))completion{
self.focusRect = focusRect;
self.completion = completion;
}
-(void)drawInContext:(CGContextRef)ctx{
UIBezierPath * focusPath = [self createBezierPathWithBounds:self.bounds focusRect:self.focusRect];
CGContextAddPath(ctx, focusPath.CGPath);
CGContextSetFillColorWithColor(ctx, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5].CGColor);
CGContextEOFillPath(ctx);
CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);
CGContextSetLineWidth(ctx,2.0);
CGContextMoveToPoint(ctx, self.focusRect.origin.x + CORNERLENGTH, self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x,self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x, self.focusRect.origin.y + CORNERLENGTH);
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width - CORNERLENGTH, self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width, self.focusRect.origin.y);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width, self.focusRect.origin.y + CORNERLENGTH);
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width, self.focusRect.origin.y + self.focusRect.size.height - CORNERLENGTH);
CGContextAddLineToPoint(ctx,self.focusRect.origin.x + self.focusRect.size.width,self.focusRect.origin.y + self.focusRect.size.height);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + self.focusRect.size.width - CORNERLENGTH, self.focusRect.origin.y + self.focusRect.size.height);
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, self.focusRect.origin.x, self.focusRect.origin.y - CORNERLENGTH + self.focusRect.size.height);
CGContextAddLineToPoint(ctx,self.focusRect.origin.x,self.focusRect.origin.y+self.focusRect.size.height);
CGContextAddLineToPoint(ctx, self.focusRect.origin.x + CORNERLENGTH, self.focusRect.origin.y + self.focusRect.size.height);
CGContextStrokePath(ctx);
UIBezierPath * beziper = [UIBezierPath bezierPathWithRect:self.focusRect];
CGContextAddPath(ctx, beziper.CGPath);
CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(ctx, 0.5);
CGContextStrokePath(ctx);
}
-(UIBezierPath *)createBezierPathWithBounds:(CGRect)bounds focusRect:(CGRect)focusRect{
UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:bounds];
UIBezierPath * focusPath = [UIBezierPath bezierPathWithRect:focusRect];
[bezierPath appendPath:focusPath];
return bezierPath;
}
-(void)startAnimate{
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
self.lineLayer.frame = CGRectMake(self.focusRect.origin.x, self.focusRect.origin.y, self.focusRect.size.width, 1);
self.lineLayer.hidden = false;
[CATransaction commit];
CABasicAnimation * animation = [CABasicAnimation animation];
animation.keyPath = @"position.y";
animation.toValue = @(self.focusRect.origin.y + self.focusRect.size.height);
animation.duration = 1.5;
animation.repeatCount = HUGE_VALF;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.lineLayer addAnimation:animation forKey:@"linePositionAnimation"];
}
-(void)stopAnimate{
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
self.lineLayer.hidden = true;
[CATransaction commit];
[self.lineLayer removeAllAnimations];
}
@end