Core Animation 五 (美化图层,用动作实现自定义动画、为自定义的属性添加动画以及线程)

来源:互联网 发布:淘宝网广场舞扇子 编辑:程序博客网 时间:2024/05/16 15:11

美化图层

CALayer相对于UIView有一个主要优点,即便你工作在2D环境中,CALayer也支持自动边框效果。比如说,CALayer可以自动生成圆角、彩色边线以及阴影。所有这些都可应用动画效果,可以提供非常好的视觉体验。举个例子,你可以在用户点击并释放图层时出发更改位置和阴影的动画效果

CALayer *layer = [CALayer layer];layer.frame = CGRectMake(100,100,100,100);layer.cornerRadius = 10;layer.backgroundColor = [[UIColor redColor] CGColor];layer.borderColor = [[UIColor blueColor] CGColor];layer.borderWidth = 5;layer.shadowOpacity = 0.5;layer.shadowOffset = CGSizeMake(3.0,3.0);[self.view.layer addSublayer:layer];

用动作实现自动动画

隐式动画大多数情况下能达到你的要求,不过有时你还要配置它们。可以通过CATransaction关闭所有隐式动画,不过这只对当前事务(通常就是当前的运行循环)有效。如果要修改隐式动画行为,尤其是想让它针对该图层一直保持这种行为,就需要配置图层的动作。这样,可以在创建图层的时候就配置动画,而不需要每次更改一个属性都应用一个显式动画。

图层动作会相应图层上的各种变化,比如添加或移除图层或者修改某个属性。丽日,假设修改position属性,默认动作是执行动画的0.25秒。在下面的代码中,CirecleLayer是一个在中间依据指定的radius(半径)绘制红色圆圈的图层。

CircleLayer *circleLayer = [CircleLayer new];circleLayer.radius = 20;circleLayer.frame = self.view.bounds;[self.view.layer addSubLayer:circleLayer];...[circleLayer setPosition:CGPointMake(100,100)];//我们来修改它,以使更改位置时动画一直是2秒CircleLayer *circleLayer = [CircleLayer new];circleLayer.radius = 20;circleLayer.frame = self.view.bounds;[self.view.layer addSubLayer:circleLayer];CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];anim.duration = 2;NSMutableDictionary *actions = [NSMutableDictionary dictionaryWithDictionary:[circleLayer actions]];[actions setObject:anim forKey:@"position"];circleLayer.actions = actions;...[circleLayer setPosition:CGPointMake(100,100)];

设置动作为[NSNull null]可以禁用这个属性的隐式动画。字典中不可以保存nil,所以必须使用NSNull类。

有一些特殊的动作用于图层树中添加图层(KCAOnOrderIn)或移除图层(KCAOnOrderOut)时,举个例子,可以创建一组变大同时淡入的动画:

CABasicAnimation *fadeAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];fadeAnim.fromValue = [NSNumber numberWithDouble:0.4];fadeAnim.toValue = [NSNumber numberWithDouble:1.0];CABasicAnimation *growAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];growAnim.fromValue = [NSNumber numberWithDouble = 0.8];growAnim.toValue = [NSNumber numberWithDoule:1.0];CAAnimationGroup *groupAnim = [CAAnimationGroup animation];groupAnim.animations = [NSArray arrayWithObjects:fadeAnim,growAnim,nil];[actions setObject:groupAnim forKey:KCAOnOrderIn];

在图层替换时,动作对处理过渡(KCATransition)也非常重要。一般都是与CATransition(一个特殊类型的CAAnimation)一起使用。可以针对contents属性使用CATransition动作来创建特效,比如内容改变时的幻灯片放映效果。默认是启动淡入淡出。


为自定义属性添加动画


Core Animation 隐式地为很多图层属性添加动画,但CALayer子类的自定义属性呢,比如CircleLayer中的radius属性?默认情况下,radius是没有动画的,而contents有(通过CATransition)。因此,更改半径会导致圆形渐渐消失并出现新的图形。这可能不是你想要的结果,你可能希望radius的动画效果像position一样,通过以下几步就可以完成

@implementation CircleLayer@dynamic radius;-(id)init{self = [super init];if(self){[self setNeedsDisplay];}}-(id)initWithLayer:(id)layer{self = [super initWithLayer:layer];[self setRadius:[layer radius]];return self;}-(void)drawInContext:(CGContextRef)ctx{CGContextSetFillColorWithColor(ctx,[[UIColor redColor]CGColor]);CGFloat radius =self.radius;CGRect rect;rect.size = CGSizeMake(radius,radius);rect.origin.x = (self.bounds.size.width - radius)/2;rect.origin.y = (self.bounds.size.height - radius)/2;CGContextAddEllipseInRect(ctx,rect);CGContextFillPath(ctx);}+(BOOL)needsDisplayForKey:(NSString *)key{if([key isEqualToString:@"radius"]){return YES;}return [super needsDisplayForKey:key];}-(id<CAAction>)actionForKey:(NSString *)key{if([self presentationLayer] != nil){     if([key isEqualToString:@"radius"])     {           CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath@"radius"];            anim.fromValue = [[self presentationLayer] valueForKey:@"radius"];            return animo;      }}return [super actionForKey:key];}@end
先来回顾以下基础知识。在init方法里调用setNeedsDisplay,这样图层的drawInContext会在第一次添加图层到图层树时被调用。覆盖needsDisplayForKey:方法,这样无论如何修改半径都可以自动重绘。

Core Animation为了生成动画效果会创建图层的多个副本。它使用initWithLayer:来实现复制,因此你需要实现这个方法来复制自定义的属性。

现在要修改动作了。我们实现了actionForKey:方法,一次返回一个在当前图(presentationLayer)中有半径起始值(fromValue)的动画。这意味如果动画中途变化,动画效果会更加平滑。


Core Animation 与线程

Core Animation可以很好的适应线程。通常,可以在任意线程中修改CALayer属性,这点与UIView属性不同。可以在任何线程中调用drawInContext:方法。(不过特定的CGContext只能一次在一个线程上修改。)对CALayer属性的更改会使用CATransaction按事务分配到多个线程中进行处理。如果有一个运行循环的话,这个过程就会自动发生;如果没有运行循环,则需要定期调用[CATransaction flush]。如果可能,应该在运行循环的线程中实现Core Animation动作来改善性能。

0 0
原创粉丝点击