iOS 动画篇 - UIKit动画(一)

在前面文章中,有详细介绍过 Core Animation 动画,UIKit 动画实质上是针对核心动画的封装,不同的是,核心动画操作的是图层级别(CALayer),通常情况下,它不会影响到视图层,而 UIKit 针对的是视图级别(UIView),我们在做此动画后,视图的 frame、center等属性都会响应的变化。

在入场开发中,UIKit 动画块可以完成百分之九十以上的动画任务,这些动画任务通常是针对视图的可动画属性,例如改变视图的frame、center、bounds、backgroundColor、alpha、transform等等。在某些特殊的情况下(如改变视图的阴影、圆角等),UIKit 完成不了的时候,你依旧可以借助核心动画来完成。

由于 UIKit 动画是核心动画的一层封装,使用起来相比于核心动画要简单很多,关于一些原理的知识,你可以查看 Core Animation 动画,此篇文章只做 UIKit 动画的使用介绍。

UIView 动画

UIView 动画是对 UIView 的分类扩展。你可以使用视图的类方法对任意的视图类做属性动画而无需 Core Animation 那种创建动画实例。

begin/commit

在做动画时候,你需要将视图的可动画属性的新值放置在以后两个方法中:

+beginAnimations:context:

该方法是动画设置的开始起点,两个参数分别是动画标志符和额外参数

+commitAnimations

该方法是动画设置的介绍终点,调用该方法,表示提交当前动画的所有设置,动画入栈,等待 runloop 的执行。

// 开始设置动画
[UIView beginAnimations:@"animations" context:nil];
// 可动画属性设置预期的新值
self.blueView.center = self.view.center;
self.blueView.backgroundColor = UIColor.redColor;
// 提交动画
[UIView commitAnimations];

iOS 动画篇 - UIKit动画(一)_第1张图片

在上述例子中,我们改变了视图的中心点位置以及填充色。运行我们发现,视图达到我们预期所设置的新值。但是我们发现,这过程太过短暂,因为上述例子中使用了系统默认的动画时间,即0.2秒。接下来我们通过方法+setAnimationDuration:来改变动画的时间。

[UIView beginAnimations:@"animations" context:nil];
// 设置动画时间
[UIView setAnimationDuration:1.0];
self.blueView.center = self.view.center;
self.blueView.backgroundColor = UIColor.redColor;
[UIView commitAnimations];

iOS 动画篇 - UIKit动画(一)_第2张图片

此时由于动画时长被设置为1秒,使得动画能够平滑动转变为新值。

需要注意的是:设置动画的时长需要放置在属性设置之前,否则无效。动画其他设置大部分都是如此。

类似的,我们还可以使用其他方法来设置我们的动画,其中有:

+setAnimationDuration: //动画时长,默认为0.2
+setAnimationDelay: //延迟动画,默认为0.0
+setAnimationStartDate: //指定日期开始动画,默认为当前时间
+setAnimationCurve: //动画的时间速率,默认为UIViewAnimationCurveEaseInOut
+setAnimationRepeatCount: //重复次数,默认为0
+setAnimationRepeatAutoreverses: //是否执行反转动画
+setAnimationsEnabled: //动画执行能力,可关闭动画

动画过程

除此之外,如果我们想要监听动画的执行过程事件,该怎么办呢?UIKit 动画为我们提供了三个方法,别分用来设置动画的代理监听对象以及动画的开始和结束事件。

+setAnimationDelegate: //设置动画代理
+setAnimationWillStartSelector: //动画开始事件
+setAnimationDidStopSelector: //动画结束事件

示例:我们为动画添加代理,实现动画的结束代理方法

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    [UIView beginAnimations:@"animations" context:nil];
    [UIView setAnimationDuration:1.0];
    self.blueView.center = self.view.center;
    self.blueView.backgroundColor = UIColor.redColor;
    // 设置动画代理
    [UIView setAnimationDelegate:self];
    // 设置动画开始和结束代理
    [UIView setAnimationWillStartSelector:@selector(start:)];
    [UIView setAnimationDidStopSelector:@selector(end:)];
    [UIView commitAnimations];
}

-(void)start:(NSString*)flag{
//    NSString* tip = [NSString stringWithFormat:@"动画开始-%@",flag];
//    [[[UIAlertView alloc] initWithTitle:@"提示" message:tip delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil] show];
}
-(void)end:(NSString*)flag{
    NSString* tip = [NSString stringWithFormat:@"动画结束-%@",flag];
    [[[UIAlertView alloc] initWithTitle:@"提示" message:tip delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil] show];
}

iOS 动画篇 - UIKit动画(一)_第3张图片

和其他方法不同,设置动画代理的三个方法只要begin/commit之间即可。

转场动画

UIKit 动画中还有一个转场动画,该类通常需要两个或多个视图。

+setAnimationTransition:forView:cache:

参数 transition:是转场类型,有以下几个:

typedef enum UIViewAnimationTransition : NSInteger {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown
} UIViewAnimationTransition;

参数 forView:转场动画所应用到的视图

参数 cache:缓存可以提高性能,但如果将此参数设置为YES,则不得在转换期间更新视图或其子视图,因为更新视图及其子视图可能会干扰缓存行为,并导致视图内容在动画期间被错误地(或在错误的位置)呈现,必须等到转换结束才能更新视图。如果为“否”,则必须为过渡动画的每个帧更新视图及其内容,这可能会显着影响帧速率。

如果你想从一个视图翻转到另一个视图,以下为系统提供的步骤:

  1. Begin an animation block.

  2. Set the transition on the container view.

  3. Remove the subview from the container view.

  4. Add the new subview to the container view.

  5. Commit the animation block.

无非就是对容器视图子视图的移除和添加操作。我们这里例子只有两个视图,因此采用另外方式:做透明度的设置,在翻转的时候隐藏其中一个。

[UIView beginAnimations:@"animations" context:nil];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.blueView cache:NO];
self.redView.alpha = !self.redView.alpha;
[UIView setAnimationDuration:1.0];
[UIView commitAnimations];

iOS 动画篇 - UIKit动画(一)_第4张图片

下面演示了不同的转场动画效果:

iOS 动画篇 - UIKit动画(一)_第5张图片

上述就是对 UIView 的简单介绍和使用了。UIView 动画通常只能够对视图属性的可动画属性有效,如果我们想实现例如关键帧,弹性动画等,似乎很困难。另外如果我们想要在一个动画结束之后,紧接着开始另一段动画(甚至更多的动画),我们需在代理方法中加上动画标志符加以区分,或者需要更多的方法选择器指向不同方法,无论是哪一种方式,似乎都加深了代码的复杂度。

出于上述的问题考虑,苹果在 iOS4.0 之后推出了 UIView 动画块,并且强烈推荐开发者使用新的 UIView 动画块来实现视图动画。新的动画方式对之前的动画再次进行了封装,让我们做动画时不在需要进行begin/commit以及设置动画属性时调用大量的代码,而采用了诸多的快捷创建方式。另外将动画的完成事件通过block块传递,从而可以一对一的形式传递动画过程。使用动画块的另外一个好处就是我们可在完成块中继续使用新的动画块,从而执行新的动画。

UIView 动画块不仅提供属性动画,对应 Core Animation 中的 CAKeyframeAnimation 关键帧动画、CASpringAnimation 弹性动画等都提供了相应的方法,这让我们实现的动画效果变得更多,使用更简便了。

UIView 动画块

属性动画

UIView 动画块依旧是视图的类方法,如果你想要简洁的做一些视图属性动画,可以使用下面的方法。

+animateWithDuration:animations:

在此方法中,你可以设置动画的时间和需要执行的属性动画。

示例:

[UIView animateWithDuration:1.0 animations:^{
    self.blueView.center = self.view.center;
    self.blueView.backgroundColor = UIColor.redColor;
}];

iOS 动画篇 - UIKit动画(一)_第6张图片

但是我们注意到,此方法中并没有动画完成的回调,如果我需要在一个动画结束之后做一些其他的事情,如执行另外一个动画。此时你需要使用下面的方法:

+animateWithDuration:animations:completion:

该方法相比于前一个方法,多个一个完成块,该完成块会在动画完成后回调。

[UIView animateWithDuration:1.0 animations:^{
    self.blueView.center = self.view.center;
    self.blueView.backgroundColor = UIColor.redColor;
} completion:^(BOOL finished) {
    // 执行另外一个动画
    [UIView animateWithDuration:1.0 animations:^{
        CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI);
        transform = CGAffineTransformScale(transform, 0.65, 1.0);
        self.blueView.transform = transform;
    }];
}];

iOS 动画篇 - UIKit动画(一)_第7张图片

但是,该方法似乎还是不可以满足我们的动画需求,因此次方法无法设置动画延迟、动画速率等。实际上,苹果提供了一个动画参数相对较多的动画方法,上述的两个方法只不过为了满足一些简单的属性动画而在该方法中封装了一层而已。

+animateWithDuration:delay:options:animations:completion:

该方法提供了动画时长、延迟动画时间、动画块、完成块等,其中还有一个可选参数 options,该参数是 UIViewAnimationOptions 类型,是一个枚举类型,参数分为三类,可以组合使用:

// 1.常规动画属性设置(可以同时选择多个进行设置)
UIViewAnimationOptionLayoutSubviews:动画过程中保证子视图跟随运动。
UIViewAnimationOptionAllowUserInteraction:动画过程中允许用户交互。
UIViewAnimationOptionBeginFromCurrentState:所有视图从当前状态开始运行。
UIViewAnimationOptionRepeat:无限重复运行动画。
UIViewAnimationOptionAutoreverse :动画运行到结束点后仍然以动画方式回到初始点。
UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套动画时间设置。
UIViewAnimationOptionOverrideInheritedCurve:忽略嵌套动画速度设置。
UIViewAnimationOptionAllowAnimatedContent:动画过程中重绘视图(注意仅仅适用于转场动画)。 
UIViewAnimationOptionShowHideTransitionViews:视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅仅适用于转场动画)
UIViewAnimationOptionOverrideInheritedOptions :不继承父动画设置或动画类型。
// 2.动画速度控制(可从其中选择一个设置)
UIViewAnimationOptionCurveEaseInOut:动画先缓慢,然后逐渐加速。
UIViewAnimationOptionCurveEaseIn :动画逐渐变慢。
UIViewAnimationOptionCurveEaseOut:动画逐渐加速。
UIViewAnimationOptionCurveLinear :动画匀速执行,默认值。
// 3.转场类型(仅适用于转场动画设置,可以从中选择一个进行设置,基本动画、关键帧动画不需要设置)
UIViewAnimationOptionTransitionNone:没有转场动画效果。
UIViewAnimationOptionTransitionFlipFromLeft :从左侧翻转效果。
UIViewAnimationOptionTransitionFlipFromRight:从右侧翻转效果。
UIViewAnimationOptionTransitionCurlUp:向后翻页的动画过渡效果。   
UIViewAnimationOptionTransitionCurlDown :向前翻页的动画过渡效果。   
UIViewAnimationOptionTransitionCrossDissolve:旧视图溶解消失显示下一个新视图的效果。   
UIViewAnimationOptionTransitionFlipFromTop :从上方翻转效果。   
UIViewAnimationOptionTransitionFlipFromBottom:从底部翻转效果。

示例:我们使用上述方法来执行动画的时间速率和重复行为。

[UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionCurveEaseIn animations:^{
    self.blueView.center = self.view.center;
    self.blueView.backgroundColor = UIColor.redColor;
} completion:^(BOOL finished) {
    // do something
}];

iOS 动画篇 - UIKit动画(一)_第8张图片

在参数选项中,我们组合使用了 UIViewAnimationOptionRepeat 和 UIViewAnimationOptionCurveEaseIn,即重复和缓慢进入的动画效果。

一般来说,前两种方法的组合使用就可以完成大部分的属性动画,第三种用到的时候相对较少。

转场动画

在 iOS4.0 时期,除了上述的的属性动画,还有针对+setAnimationTransition:forView:cache:转场动画的封装。

+transitionWithView:duration:options:animations:completion:

示例:

[UIView transitionWithView:self.blueView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
    self.redView.alpha = !self.redView.alpha;
} completion:nil];

该方法需要指定转场动画的视图 view,需要animations动画块中执行转场动画,通常是对内容视图containerView子视图的移除和添加,就像下面的示例:

官方示例:

[UIView transitionWithView:containerView
           duration:0.2
           options:UIViewAnimationOptionTransitionFlipFromLeft
           animations:^{ [fromView removeFromSuperview]; [containerView addSubview:toView]; }
           completion:NULL];

在前一节的 UIView 动画中,系统提供了四种转场动画,而在 iOS4.0 的动画块中,系统提供了另外三种转场动画:

iOS 动画篇 - UIKit动画(一)_第9张图片

除此方法,系统还提供了另外一种转场动画方法,此方法关注点是fromViewtoView,即从某个视图转场到某个视图,其中应用转场动画视图为fromView的父视图。

示例1:


@interface ViewController ()
@property(strong, nonatomic)UIView* baseView;
@property(strong, nonatomic)UIView* fromView;
@property(strong, nonatomic)UIView* toView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)];
    [self.view addSubview:self.baseView];
    self.baseView.center = self.view.center;
    
    self.fromView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)];
    [self.baseView addSubview:self.fromView];
    self.fromView.backgroundColor = [UIColor redColor];
    
    self.toView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)];
    self.toView.backgroundColor = [UIColor yellowColor];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    [UIView transitionFromView:self.fromView
                        toView:self.toView
                      duration:1.0
                       options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
                       }];
}

iOS 动画篇 - UIKit动画(一)_第10张图片

示例2:切换导航控制器

通常我们切换导航控制器时,如果直接设置应用的根控制器的话,是没有任何动画效果的,刷的一下很突然,因此我们通过使用此方法来演示转场动画的使用。

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    // 创建新的导航控制器
    NextPageViewController* next = [NextPageViewController new];
    UINavigationController* navNew = [[UINavigationController  alloc] initWithRootViewController:next];
    // 获取当前的导航控制器
    UITabBarController* tabCtrl = (UITabBarController*)[[UIApplication sharedApplication] keyWindow].rootViewController;
    // 设置转场动画
    [UIView transitionFromView:tabCtrl.view toView:navNew.view duration:1.0 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) {
        // 将新的导航控制器设置为 keyWindow 的根控制器
        UIWindow* keyWindow = [[UIApplication sharedApplication] keyWindow];
        keyWindow.rootViewController = navNew;
    }];
}

iOS 动画篇 - UIKit动画(一)_第11张图片

以上就是 iOS4.0 推出的 UIView 动画块,主要是针对 iOS2.0 中 UIView 动画的封装。

在 iOS7.0,苹果又推出了弹性动画和关键帧动画,以及消除隐式动画的方法。

弹性动画

弹性动画依旧是沿用了视图的类方法的调用形式,在 Core Animaiton 中与之相对应的是 CASpringAnimation,不过 CASpringAnimation 是在 iOS9.0 后才推出来,稍晚于 UIView 的动画块。

+animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:

该方法中出现了两个新的参数名:

参数 dampingRatio 表示阻尼比,取值在(0,1),期值越小振荡的幅度和次数会增加,反之则减少。

参数 velocity 表示视图的初始速度。对于这个值的解释,官方文档的说明与实际效果并不一致,不过速度越大,振幅越大。

在使用弹性动画过程中,建议调整动画时间(影响运行速度)和阻尼比。

示例:

[UIView animateWithDuration:0.5
                      delay:0
     usingSpringWithDamping:0.3
      initialSpringVelocity:0
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
    self.blueView.center = self.view.center;
} completion:nil];

iOS 动画篇 - UIKit动画(一)_第12张图片

关键帧动画

和之前的的几种动画不同,关键帧动画关注的不仅仅是开始和结束的位置,关键帧动画可设置的位置有多个,因此在设置关键帧动画时,一个方法必然是非常困难的。苹果将关键帧动画拆成了两个,一个用来设置动画的时长、运行模式等,另外一个用来设置视图在每一帧的新值。

关键帧动画的基本设置:

+animateKeyframesWithDuration:delay:options:animations:completion:

该方法用来设置关键帧动画的基本情况,其中包括动画执行的总时长、延迟时间、运行模式、动画块、完成块。

在动画块中,你可以设置关键帧的动画情况。当然你也可以在该动画块中设置新值,就像下面这样:

[UIView animateKeyframesWithDuration:1.0 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
    self.blueView.backgroundColor = UIColor.redColor;
    self.blueView.center = self.view.center;
    self.blueView.transform = CGAffineTransformMakeScale(0.6, 1.0);
} completion:nil];

这表示你的关键帧只有开始(初始状态)和结束,但是这和之前的属性动画并无二致:+animateWithDuration:animations:,那又何必使用关键帧动画呢?

iOS 动画篇 - UIKit动画(一)_第13张图片

如果只关注视图的开始和结束状态,那么使用关键帧动画似乎显得有点大材小用,关键帧的作用是可以让你设置多个位置的状态的,并且无需动画嵌套(属性动画在完成后继续嵌套新动画),而是将整个过程作为一个完成的动画。

添加关键帧:

+addKeyframeWithRelativeStartTime:relativeDuration:animations:

参数 frameStartTime 和 frameDuration 都是相对的,相对整个动画时间。

frameStartTime:指当前帧的启动时间,如0表示动画的开始位置,0.5表示动画执行一半的时刻。

frameDuration:指动画到指定值的时间长度,如0.5表示相对动画总时长的一半时间。

示例:设置视图的多个位置的状态

[UIView animateKeyframesWithDuration:1.0 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
    // 动画的 0 - 0.25 的部分
    [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.25 animations:^{
        self.blueView.center = CGPointMake(self.blueView.center.x+200, self.blueView.center.y);
        self.blueView.transform = CGAffineTransformMakeScale(0.6, 1.0);
    }];
    // 动画的 0.25 - 0.5 的部分
    [UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.25 animations:^{
        self.blueView.center = CGPointMake(self.blueView.center.x, self.blueView.center.y-200);
        self.blueView.transform = CGAffineTransformIdentity;
    }];
    // 动画的 0.5 - 0.75 的部分
    [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{
        self.blueView.center = CGPointMake(self.blueView.center.x-200, self.blueView.center.y);
        self.blueView.transform = CGAffineTransformMakeRotation(M_PI_2);
    }];
    // 动画的 0.75 - 1 的部分
    [UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
        self.blueView.center = CGPointMake(self.blueView.center.x, self.blueView.center.y+200);
        self.blueView.transform = CGAffineTransformIdentity;
    }];
    // 动画的 0 - 1 的部分
    [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:1.0 animations:^{
        self.blueView.backgroundColor = UIColor.redColor;
    }];
} completion:nil];

iOS 动画篇 - UIKit动画(一)_第14张图片

这里有一个参数 options,是UIViewKeyframeAnimationOptions 类型,和上面属性动画参数设置有些差别,关键帧动画设置参数分为两类,也可以组合使用:

// 1.常规动画属性设置(可以同时选择多个进行设置)
UIViewAnimationOptionLayoutSubviews:动画过程中保证子视图跟随运动。
UIViewAnimationOptionAllowUserInteraction:动画过程中允许用户交互。
UIViewAnimationOptionBeginFromCurrentState:所有视图从当前状态开始运行。
UIViewAnimationOptionRepeat:重复运行动画。
UIViewAnimationOptionAutoreverse :动画运行到结束点后仍然以动画方式回到初始点。
UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套动画时间设置。
UIViewAnimationOptionOverrideInheritedOptions :不继承父动画设置或动画类型。
// 2.动画模式设置(同前面关键帧动画动画模式一一对应,可以从其中选择一个进行设置)
UIViewKeyframeAnimationOptionCalculationModeLinear:连续运算模式。
UIViewKeyframeAnimationOptionCalculationModeDiscrete :离散运算模式。
UIViewKeyframeAnimationOptionCalculationModePaced:均匀执行运算模式。
UIViewKeyframeAnimationOptionCalculationModeCubic:平滑运算模式。
UIViewKeyframeAnimationOptionCalculationModeCubicPaced:平滑均匀运算模式。

看过之前的 Core Animation 的小伙伴肯定还知道 CAKeyframeAnimation 关键帧动画,还可以将关键帧无限增加从而演变为路径关键帧动画,然而关于这一点,目前 UIView 动画块还不支持,所以设计到路径的动画还是依赖 Core Animation 核心动画来实现。


相关阅读

  • iOS 动画篇 - Core Animation

  • iOS 动画篇 - UIKit动画(一)

  • iOS 动画篇 - UIKit动画(二)

  • iOS动画篇 - pop动画库

你可能感兴趣的:(iOS_知识补充)