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

本文介绍了如何利用CALayer的特性实现各种动画效果,包括自动边框效果、自定义属性动画及多线程支持等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

美化图层

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动作来改善性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值