首页 > 代码库 > iOS动画简介

iOS动画简介

在iOS上实现动画效果,基本都是在一段给定的时间内完成状态的连续变化,包括背景色、Frame大小、位移、旋转、透明度、缩放等。

老的动画实现形式:

iOS 4.0之前,苹果提供的是类似于数据库中的事务编程的模式:

例如实现一个view的淡入效果,可以通过设置view的alpha实现(alpha = 1是全透明):

    [UIView beginAnimations:nil context: nil];

    [UIView setAnimationDuration:1.0];    //动画的时长是1s
    [UIView setAnimationDelay:0.0];  //不延时执行
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationStopped)];//动画完成后执行一些操作
    self.view.alpha = 0.0;//最终的alpha为0,不透明
    self.view.frame = CGRectMake(10, 10, 50, 50);//最终的frame
     
    [UIView commitAnimations];//提交动画

实现了一个1s中的动画,将view的透明度设为不透明,frame设为(10,10,50,50)。

beginAnimations标识开始动画,然后设置动画的各种属性,最后通过commitAnimations提交动画,之后系统接管该动画并执行。

Block动画实现形式:

iOS4.0之后,苹果提供了一组重载函数,实现了以block的形式执行动画:

(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

比如下面这样:

+ (void)fadeIn: (UIView *)view andAnimationDuration: (float) duration{
    [view setAlpha:0.0];
    NSLog(@"begin animation");
    [UIView animateWithDuration:duration animations:^{
        [view setAlpha:1.0];
        NSLog(@"in animation block");
    } completion:^(BOOL finished) {
        NSLog(@"completion animation");
    }];
    NSLog(@"exit fadeIn");
}

通过参数duration、delay、options等分别设置动画的时间、延时、执行选项。

在block animations中实现动画的主体,在block completion中实现动画完成后需要执行的逻辑。之所以有completion,是给我们一个在动画跑完成后执行一些逻辑的入口(老的动画方式通过setAnimationDidStopSelector实现)。

假如你想在动画执行完成后做一些事情,那么把代码放在NSLog(@"in animation block")和NSLog(@"exit fadeIn")是有本质区别的。因为我们把动画组装好后就交给操作系统了,会立刻走到NSLog(@"exit fadeIn"),并不会阻塞主线程流程。

completion block会有一个BOOL类型参数,用来告知动画是否真的执行完成了。这个参数的意义在于我们可以在completion中判断之前的动画是否真的执行完了,因为动画是有可能被取消的(可以通过[view.layer removeAllAnimations]取消动画),但无论是否取消,系统都会调用completion告知动画执行结束(想象扔手雷的场景,在与地面发生碰撞时手雷飞行的动画结束,接下来要执行爆炸的动画,但如果手雷飞行时被人接住,那么就需要取消飞行动画,至于后面是否爆炸......)。甚至如果我们设置了动画执行的时间duration是0时,completion也会被调用(虽然duration是0,但仍是异步的,系统会在下一个消息循环的开始时立刻调用completion),此时BOOL参数仍然是YES。

前面说到提交动画给系统后,动画并不会阻塞主线程,但有时候我们希望在动画结束前不要有其他逻辑进来,这可以通过在提交动画后执行消息循环轮询变量状态实现:

+ (void)fadeIn: (UIView *)view andAnimationDuration: (float) duration andWait:(BOOL) wait{
    __block BOOL done = wait; //wait =  YES wait to finish animation
    [view setAlpha:0.0];
    [UIView animateWithDuration:duration animations:^{
        [view setAlpha:1.0];
    } completion:^(BOOL finished) {
        done = NO;
    }];
    // wait for animation to finish
    while (done == YES)
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}

常见的动画实现:

缩放--改变view.transform

view.transform = CGAffineTransformMakeScale(0, 0);
    [UIView animateWithDuration:duration animations:^{
        view.transform = CGAffineTransformIdentity;

    } completion:^(BOOL finished) {
    }];

淡入淡出--改变view.alpha

[view setAlpha:0.0];
    [UIView animateWithDuration:duration animations:^{
        [view setAlpha:1.0];
    } completion:^(BOOL finished) {
    }];

位置移动--改变view.center

[UIView animateWithDuration:duration animations:^{
        view.center = CGPointMake(view.center.x - length, view.center.y);
        
    } completion:^(BOOL finished) {
    }];

旋转--改变view.transform

[UIView animateWithDuration:duration animations:^{
        view.transform = CGAffineTransformMakeRotation(degreesToRadians(angle));
    } completion:^(BOOL finished) {
    }];

动画嵌套:

在一个动画执行完成后继续执行动画,甚至执行多个动画形成动画链:

这个带回弹的气泡弹出动画通过串行的3个改变view缩放值的动画实现,在completion中继续一个新的动画。

self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
    __unsafe_unretained NearbyGroupMapAnnotationView* reself = self;
    [UIView animateWithDuration:0.3/1.5 animations:^{
        reself.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1);
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.3/2 animations:^{
            reself.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.9, 0.9);
        } completion:^(BOOL finished) {
            [UIView animateWithDuration:0.3/2 animations:^{
                reself.transform = CGAffineTransformIdentity;
            }];
        }];
    }];

首先将view缩放到正常状态的0.001倍作为初始态,

动画1:开始将view放到到1.1倍

动画2:将view缩小到0.9倍

动画3:将view回复正常大小

减速动画

(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

这个函数可以通过下面两个很有意思的参数实现类似弹簧的效果(带加速度):

usingSpringWithDamping:(CGFloat)dampingRatio
initialSpringVelocity:(CGFloat)velocity

上图中有两个动画效果,一个是平稳的减速停止,一个是减速后带有回弹(类似碰撞)。

usingSpringWithDamping标识了弹簧的强度,介于0~1之间为1时,动画将会平滑且没有上下摆动的减速到他的最终状态,当该值小于1时,动画的摆动会越来越厉害。

initialSpringVelocity用来标识view开始收到弹簧作用力时的速度介于0~1之间,假设整个动画将移动100,当取1时,表示速度为100/1s。initialSpringVelocity越小速度越小。

其实,不用上面这个复杂的方法,通过串行多个相反方向的位移动画,(分别控制他们的动画时间模拟加速度),也可以实现回弹的效果。

iOS动画简介