iOS之UIView动画

UIView动画

概述

在AppStore中的应用越来越重视动画效果的使用,一个良好动画效果可以让两个状态之间平滑地过度,也可以利用动画吸引住用户的眼球,在UIView类中共有三个类目(Category)用于实现动画功能,分为UIViewAnimation、UIViewAnimationWithBlocks以及UIViewKeyframeAnimations,他们是Apple对核心动画(Core Animation)的封装,可以让我们不进行任何绘画等复杂操作的前提下实现大部分动画需求

UIView动画的用处

UIView类中提供的动画功能,允许我们实现如下动画需求

  • 动态修改”可动画属性”
    • frame(位置及大小)
    • bounds(大小)
    • center(中心点位置)
    • transform(形变)
    • alpha(透明度)
    • backgroundColor(背景颜色)
    • contentStretch(可拉伸区域,从iOS6开始被废弃)
  • 提供转场动画效果
    • 修改已存在视图的子视图(也可以修改自身)
    • 在视图层级中用一个视图替换另一个视图(Block动画提供)
本文中样例素材

在介绍UIView动画的过程中,我们会使用一个UIImageView图片为例,对各动画效果进行演示

@property (nonatomic, strong) UIImageView *demoImageView;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.demoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, [[UIScreen mainScreen] bounds].size.width-40, [[UIScreen mainScreen] bounds].size.height-40)];
    self.demoImageView.image = [UIImage imageNamed:@"old"];
    [self.view addSubview:self.demoImageView];
}

UIViewAnimation

Apple自iOS2开始提供了若干UIView的类方法来实现动画功能,Apple建议从iOS4系统开始尽量使用Block方式来实现动画功能,因此我们以后在开发中应该遵从Apple的建议使用Block方式来实现动画功能,但是学习本部分对未来学好Block动画有很好的铺垫作用,因此本文在这里会对系统提供的方法进行详尽地介绍

在iOS4之前,我们需要使用动画块(begin/commit animation block)来进行动画,一个完整的动画块由以下四部分组成

  • 动画块开始标识
  • 配置动画选择项
  • 修改可动画属性
  • 动画块结束标识
动画块开始标识

标识动画块的开始,该方法是告诉系统将要执行一个或多个动画,如果通过setAnimationWillStartSelector:或者setAnimationDidStopSelector:方法设置了代理回调方法,便可以在回调方法中收到该方法中的两个参数animationID(动画标志)和context(上下文,一般为nil)

// 格式
+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;
// 样例
[UIView beginAnimations:@"demo" context:nil];
动画块结束标识

标识动画块的结束,同时安排动画执行

// 格式
+ (void)commitAnimations;
// 样例
[UIView commitAnimations];
设置动画代理对象及代理回调方法

设置动画代理对象,以便在动画开始和结束时收到系统回调

// 格式
+ (void)setAnimationDelegate:(nullable id)delegate; 
// 样例
[UIView setAnimationDelegate:self];

设置动画将要开始时调用代理对象的方法

// 格式
+ (void)setAnimationWillStartSelector:(nullable SEL)selector; 
// 样例
[UIView setAnimationWillStartSelector:@selector(animationWillStart:context:)];

注1: 当动画当前不可用时,该方法不会被调用

注2: 当不设置该方法时,系统默认会调用- (void)animationWillStart:(NSString )animationID context:(void )context

设置动画已经结束后调用代理对象的方法

// 格式
+ (void)setAnimationDidStopSelector:(nullable SEL)selector; 
// 样例
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

注1: 当动画当前不可用时,该方法依然会被调用

注2: 当不设置该方法时,系统默认会调用- (void)animationDidStop:(NSString )animationID finished:(NSNumber )finished context:(void *)context

设置动画持续时长

设置动画持续时长,默认0.2s

// 格式
+ (void)setAnimationDuration:(NSTimeInterval)duration; 
// 样例
[UIView setAnimationDuration:0.2f];

注: 该方法必须在修改”可变化属性”之前调用

设置延时动画时长

设置延时动画时长,默认0.0s

// 格式
+ (void)setAnimationDelay:(NSTimeInterval)delay; 
// 样例
[UIView setAnimationDelay:0.0f];

注: 该方法必须在修改”可变化属性”之前调用

设置动画开始时间(该方法设置后整个动画即不生效,不明原因)

设置动画开始时间,默认当前时间

// 格式
+ (void)setAnimationStartDate:(NSDate *)startDate; 
// 样例
[UIView setAnimationStartDate:[NSDate date]];

注1: 该方法必须在修改”可变化属性”之前调用

注2: 不要在Block动画中调用该方法

注3: 该方法设置后整个动画即不生效,作者尝试过如下四种方式,结果都不生效

[UIView setAnimationStartDate:[NSDate date]];
[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceReferenceDate:CFAbsoluteTimeGetCurrent()]];

[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceNow:3]];
[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceReferenceDate:CFAbsoluteTimeGetCurrent()+3]];
设置时间函数

时间函数即动画进行速度随着时间的变化曲线,系统采用一个枚举类型来保存

/**
 typedef NS_ENUM(NSInteger, UIViewAnimationCurve)
 {
     UIViewAnimationCurveEaseInOut // 默认,淡入淡出,先慢后快再慢
     UIViewAnimationCurveEaseIn // 淡入,先慢后快
     UIViewAnimationCurveEaseOut // 淡出,先快后慢
     UIViewAnimationCurveLinear // 线性
 };
 */

// 格式
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve; 
// 样例
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

注: 该方法必须在修改”可变化属性”之前调用

设置动画重复次数

设置动画重复次数,重复次数可以是小数,小数部分会做相应比例的动画,然后直接变化到动画结束位置

// 格式
+ (void)setAnimationRepeatCount:(float)repeatCount; 
// 样例
[UIView setAnimationRepeatCount:2.5f];

注: 该方法必须在修改”可变化属性”之前调用

设置是否反向动画

设置是否反向动画,默认为NO,如果设置为YES,动画向前做完会做一次反向动画,那么一正一反算作一次repeatCount,动画所用时长会加倍

// 格式
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses; 
// 样例
[UIView setAnimationRepeatAutoreverses:YES];

注1: 该方法必须在修改”可变化属性”之前调用

注2: 因为当动画结束后一定会定格在动画块中设置的新位置上,为了不在最后产生一次不和谐的跳动,可以将repeatCount设置成0.5次即可

设置是否从当前状态开始动画

该方法用于处理当旧动画正在运行时开始一个新的动画,是否从旧动画的当前状态开始新动画,默认为NO

  • 当设置为NO时,旧动画的结束状态会被作为新动画的初始状态,且旧动画会在新动画开始的时候立刻结束动画
  • 当设置为YES时,旧动画的当前状态会被作为新动画的初始状态
// 格式
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;
// 样例
[UIView setAnimationBeginsFromCurrentState:NO];
设置转场效果

设置转场效果,该方法有如下三个参数

  • 参数1: 转场动画方向,系统采用一个枚举类型来保存
  • 参数2: 参与转场动画的视图(转场动画效果发生在该视图上)
  • 参数3: 如果设置为YES,则视图在开始和结束时分别渲染一次,在动画过程中不可以更新视图(这样性能好),如果设置为NO,则视图每一帧都会渲染,在动画过程中可以实时更新视图(这样能力强)
/**
 typedef NS_ENUM(NSInteger, UIViewAnimationTransition)
 {
     UIViewAnimationTransitionNone,
     UIViewAnimationTransitionFlipFromLeft,
     UIViewAnimationTransitionFlipFromRight,
     UIViewAnimationTransitionCurlUp,
     UIViewAnimationTransitionCurlDown
 };
 */

// 格式
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;
// 样例
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:NO];

注: 该方法每个动画块中只可调用一次

设置是否动画可用

设置是否动画可用,默认为YES,如果设置为NO,则没有动画效果,属性变化立即生效

// 格式
+ (void)setAnimationsEnabled:(BOOL)enabled;
// 样例
[UIView setAnimationsEnabled:YES];

注: 该设置只影响在这句代码之后设置的属性变化

返回一个BOOL值标识是否动画可用

// 格式
+ (BOOL)areAnimationsEnabled;
// 样例
[UIView areAnimationsEnabled];

Apple在iOS7中新增加了一个简单的封装,该方法会先检查动画当前是否为可用状态,然后设置动画为不可用状态,执行Block中的代码,最后重新恢复动画为原来状态

// 格式
+ (void)performWithoutAnimation:(void (^)(void))actionsWithoutAnimation;
// 样例
[UIView performWithoutAnimation:^{
    self.demoImageView.alpha = 0.4;
}];
动画块的嵌套

动画块的嵌套使用,是指在commit提交前一个动画之前再次调用begin方法创建一个新的动画,该新动画可以根据需求指定与外层动画不同的”动画持续时长”“时间函数”等动画选择项,当调用commit提交一个动画时,如果当前动画块是在最外层,该方法会开始做动画,如果当前动画块嵌套在另一个动画块的里面,该方法会等待外层的动画块提交,直到最外层动画块提交之后一起做动画,动画是在单独的线程运行,这样多个动画便可以一个一个地执行起来

// 以"嵌套动画的外层历时5s将视图透明度修改为0.2,嵌套动画的内层不继承外层动画的持续时长,将视图大小缩小一半,期间重复2.5次"为例,代码如下
[UIView beginAnimations:@"parent" context:nil];
[UIView setAnimationDuration:5.0f];
self.demoImageView.alpha = 0.2f;

[UIView beginAnimations:@"demo2" context:nil];
[UIView setAnimationDuration:1.0f];
[UIView setAnimationRepeatCount:2.5f];
[UIView setAnimationRepeatAutoreverses:YES];
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView commitAnimations];

[UIView commitAnimations];

嵌套动画.png

两个小例子

例子一

// 以"将视图大小缩小一半,并做反向动画,最后平滑地停留在预定位置"为例,代码如下
[UIView beginAnimations:@"demo" context:nil];
[UIView setAnimationDuration:1.0f];
[UIView setAnimationRepeatCount:2.5];
[UIView setAnimationRepeatAutoreverses:YES];
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView commitAnimations];

demo1.png

例子二

// 以"从左侧翻转转场动画,且将视图大小缩小一半(动画效果发生在该视图上)"为例,代码如下
[UIView beginAnimations:@"demo" context:nil];
[UIView setAnimationDuration:1.0f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.demoImageView cache:YES];
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView commitAnimations];

demo2.png

UIViewAnimationWithBlocks(Block动画)

Apple从iOS4开始加入了Block功能,UIView的普通动画方法全面增加了对Block的支持,自此我们便可以非常方便地使用动画功能,推荐使用

普通属性动画
/**
 * 属性动画
 *
 * @param duration   动画持续时长
 * @param delay      延时动画时长
 * @param options    动画选择项
 * @param animations 动画Block
 * @param completion 动画执行完执行的Block
 *
 * @return 无返回值
 */
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

// 以"将视图大小缩小一半,动画结束后恢复"为例,代码如下
[UIView animateWithDuration:3.0f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
    self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
    if (finished)
    {
        self.demoView.transform = CGAffineTransformIdentity;
    }
}];

上述Block动画共有5个参数,使用起来需要设置的内容比较多,系统为我们提供了两个相对简单的Block动画以用于简单动画

// delay = 0.0,options = 0的属性动画
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

// delay = 0.0,options = 0,completion = NULL的属性动画
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations;

普通动画.png

弹性属性动画

Apple从iOS7开始在系统中大量使用SpringAnimation,该动画由于前期速度很快,可以给用户一种干净利落的感觉,所以推荐大家使用以与系统动画接轨

/**
 * SpringAnimation(弹性动画)
 *
 * @param duration     动画持续时长
 * @param delay        延时动画时长
 * @param dampingRatio 当dampingRatio设置为1时,动画会毫无震荡感地平稳地运动到最终状态,如果dampingRatio的值少于1,值越小在最终状态上震荡效果会越明显
 * @param velocity     初始速度,数值越大一开始移动速度越快(注: 初始速度取值较高而时间取值较短时,也会产生震荡效果)
 * @param options      动画选择项
 * @param animations   动画Block
 * @param completion   动画执行完执行的Block
 *
 * @return 无返回值
 */
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

// 以"将视图大小缩小一半,动画结束后恢复"为例,代码如下
[UIView animateWithDuration:3.0f delay:0.0f usingSpringWithDamping:0.7f initialSpringVelocity:5.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
    self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
    if (finished)
    {
        self.demoView.transform = CGAffineTransformIdentity;
    }
}];

弹性动画.png

执行系统动画

Apple从iOS7开始加入了一个方法,允许我们执行系统动画,且可以并行地执行附加动画

/**
 * 在一个或多个视图上执行系统动画
 *
 * @param animation          系统动画,暂时只有UISystemAnimationDelete一个
 * @param views              执行动画的views数组
 * @param options            动画选择项
 * @param parallelAnimations 并行附加动画Block
 * @param completion         动画执行完执行的Block
 *
 * @return  无返回值
 */
+ (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray<__kindof UIView *> *)views options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))parallelAnimations completion:(void (^ __nullable)(BOOL finished))completion;

// 以"执行系统删除动画"为例,代码如下
[UIView performSystemAnimation:UISystemAnimationDelete onViews:@[self.demoImageView] options:UIViewAnimationOptionCurveEaseInOut animations:^{
    // do something....
} completion:^(BOOL finished) {
    if (finished)
    {
        // do something....
    }
}];

注: 附加动画不要修改正在被系统动画修改的属性

系统动画.png

转场动画

Apple为我们提供了两个方法用于进行转场动画,为我们解决两个动画需求

  • 修改已存在视图的子视图(也可以修改自身)
/**
 * TransitionAnimation(转场动画)
 *
 * @param view       参与转场动画的视图(转场动画效果发生在该视图上)
 * @param duration   动画持续时长
 * @param options    动画选择项
 * @param animations 动画Block
 * @param completion 动画执行完执行的Block
 *
 * @return  无返回值
 */
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

// 以"父视图从左侧翻转转场动画,且将子视图大小缩小一半,动画结束后恢复"为例,代码如下
[UIView transitionWithView:self.view duration:3.0f options:UIViewAnimationOptionTransitionFlipFromLeft | UIViewAnimationOptionAllowAnimatedContent animations:^{
    self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
    if (finished)
    {
        self.demoImageView.transform = CGAffineTransformIdentity;
    }
}];

注: 该转场动画作用于view上

转场动画1.png

  • 在视图层级中用一个视图替换另一个视图
/**
 * TransitionAnimation(转场动画)
 *
 * @param fromView   参与转场动画的视图(被移除视图)
 * @param toView     参与转场动画的视图(被添加视图)
 * @param duration   动画持续时长
 * @param options    动画选择项
 * @param completion 动画执行完执行的Block
 *
 * @return  无返回值
 */
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion;

// 以"从左侧翻转转场动画,将原视图移除,添加新视图进来"为例,代码如下
UIImageView *toImageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, [[UIScreen mainScreen] bounds].size.width-40, [[UIScreen mainScreen] bounds].size.height-40)];
toImageView.image = [UIImage imageNamed:@"new"];

[UIView transitionFromView:self.demoImageView toView:toImageView duration:3.0f options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
    if (finished)
    {
        NSLog(@"TransitionAnimation finished");
    }
}];

注: 该转场动画作用于fromView的父视图上,相当于将toView添加到fromView的父视图上,并将fromView从父视图上移除

转场动画2.png

Block动画的嵌套

Block动画的嵌套使用,是指在Block动画的animations中再创建一个新的Block动画,该新动画可以根据需求指定与外层动画不同的”动画选择项”,新动画会继承外层动画的”动画持续时长”和”时间函数”,但是一些”动画选择项”不会继承(在动画选择项中提供了两个用于嵌套动画的选择项,UIViewAnimationOptionOverrideInheritedDuration代表忽略嵌套动画中外层动画的时长设置,UIViewAnimationOptionOverrideInheritedCurve代表忽略嵌套动画中外层动画的时间函数设置)

// 以"嵌套动画的外层历时5s将视图透明度修改为0.2,嵌套动画的内层不继承外层动画的持续时长,将视图大小缩小一半,期间重复2.5次"为例,代码如下
[UIView animateWithDuration:5.0f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
    self.demoImageView.alpha = 0.2f;

    [UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
        [UIView setAnimationRepeatCount:2.5];
         self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
    } completion:nil];

} completion:nil];

注: 如果不使用UIViewAnimationOptionOverrideInheritedDuration,嵌套动画的内层会直接继承外层的持续时长

嵌套动画.png

Block动画选择项
typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions)
{
    // 通用设置(可同时设置多个)
    UIViewAnimationOptionLayoutSubviews            = 1 <<  0, // 动画过程中,子视图随着一起动画
    UIViewAnimationOptionAllowUserInteraction      = 1 <<  1, // 动画过程中,允许用户交互
    UIViewAnimationOptionBeginFromCurrentState     = 1 <<  2, // 是否从旧动画的当前状态开始新动画,参考UIView的+(void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;方法
    UIViewAnimationOptionRepeat                    = 1 <<  3, // 无限次数重复动画
    UIViewAnimationOptionAutoreverse               = 1 <<  4, // 是否开启反向动画,参考UIView的+(void)setAnimationRepeatAutoreverses:方法(BOOL)repeatAutoreverses;方法
    UIViewAnimationOptionOverrideInheritedDuration = 1 <<  5, // 忽略嵌套动画中外层动画的时长设置(只适用于嵌套动画)
    UIViewAnimationOptionOverrideInheritedCurve    = 1 <<  6, // 忽略嵌套动画中外层动画的时间函数设置(只适用于嵌套动画)
    UIViewAnimationOptionAllowAnimatedContent      = 1 <<  7, // 视图每一帧都会渲染,在动画过程中可以实时更新视图(只适用于转场动画)
    UIViewAnimationOptionShowHideTransitionViews   = 1 <<  8, // 视图切换时直接隐藏旧视图并显示新视图,而不是将旧视图从父视图移除并添加新视图(只适用于转场动画)
    UIViewAnimationOptionOverrideInheritedOptions  = 1 <<  9, // 不继承动画选择项(只适用于嵌套动画)

    // 时间函数控制(只可同时设置一项),参考UIView的+(void)setAnimationCurve:(UIViewAnimationCurve)curve;方法
    UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // 默认,淡入淡出,先慢后快再慢
    UIViewAnimationOptionCurveEaseIn               = 1 << 16, // 淡入,先慢后快
    UIViewAnimationOptionCurveEaseOut              = 2 << 16, // 淡出,先快后慢
    UIViewAnimationOptionCurveLinear               = 3 << 16, // 线性

    // 转场动画方向(只适用于转场动画,只可同时设置一项),参考UIView的+(void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;方法
    UIViewAnimationOptionTransitionNone            = 0 << 20, // 默认,无效果
    UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20, // 从左侧翻转
    UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20, // 从右侧翻转
    UIViewAnimationOptionTransitionCurlUp          = 3 << 20, // 向后翻页
    UIViewAnimationOptionTransitionCurlDown        = 4 << 20, // 向前翻页
    UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20, // 旧视图溶解显示新视图
    UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20, // 从上方翻转
    UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20, // 从下方翻转
}

UIViewKeyframeAnimations(关键帧动画)

Apple从iOS7开始加入了关键帧动画,可以通过提供一个动画在多个关键帧处的属性实现一系列动画效果,两个关键帧中间会有系统自动加入补间动画

/**
 * KeyframeAnimation(关键帧动画)
 *
 * @param duration   动画持续时长
 * @param delay      延时动画时长
 * @param options    关键帧动画选择项
 * @param animations 动画Block
 * @param completion 动画执行完执行的Block
 *
 * @return 无返回值
 */
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

/**
 * 添加关键帧
 *
 * @param frameStartTime 帧动画相对开始时间,取值为0~1
 * @param frameDuration  帧动画相对持续时长,取值为0~1
 * @param animations     帧动画Block
 *
 * @return 无返回值
 */
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations;

// 以"将视图大小缩小一半,再顺时针旋转90度,再放大2倍,再逆时针旋转90度恢复原样"为例,代码如下
[UIView animateKeyframesWithDuration:3.0f delay:0.0f options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
    [UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.25f animations:^{
        self.demoImageView.transform = CGAffineTransformScale(self.demoImageView.transform, 0.5, 0.5);
    }];
    [UIView addKeyframeWithRelativeStartTime:0.25f relativeDuration:0.25f animations:^{
        self.demoImageView.transform = CGAffineTransformRotate(self.demoImageView.transform, M_PI * 0.5);
    }];
    [UIView addKeyframeWithRelativeStartTime:0.5f relativeDuration:0.25f animations:^{
        self.demoImageView.transform = CGAffineTransformScale(self.demoImageView.transform, 2, 2);
    }];
    [UIView addKeyframeWithRelativeStartTime:0.75f relativeDuration:0.25f animations:^{
        self.demoImageView.transform = CGAffineTransformRotate(self.demoImageView.transform, -M_PI * 0.5);
    }];
} completion:^(BOOL finished) {
    if (finished)
    {
        NSLog(@"TransitionAnimation finished");
    }
    else
    {
        NSLog(@"在动画执行过程中再次被触发动画,会调用这里后重新开始动画");
    }
}];

注: 关键帧动画有两种形式,UIView只支持属性关键帧动画,暂不支持路径关键帧动画

关键帧动画.png

关键帧动画选择项
typedef NS_OPTIONS(NSUInteger, UIViewKeyframeAnimationOptions)
{
    // 通用设置(可同时设置多个)
    UIViewKeyframeAnimationOptionLayoutSubviews            = UIViewAnimationOptionLayoutSubviews, // 动画过程中,子视图随着一起动画
    UIViewKeyframeAnimationOptionAllowUserInteraction      = UIViewAnimationOptionAllowUserInteraction, // 动画过程中,允许用户交互
    UIViewKeyframeAnimationOptionBeginFromCurrentState     = UIViewAnimationOptionBeginFromCurrentState, // 是否从旧动画的当前状态开始新动画
    UIViewKeyframeAnimationOptionRepeat                    = UIViewAnimationOptionRepeat, // 无限次数重复动画
    UIViewKeyframeAnimationOptionAutoreverse               = UIViewAnimationOptionAutoreverse, // 是否开启反向动画
    UIViewKeyframeAnimationOptionOverrideInheritedDuration = UIViewAnimationOptionOverrideInheritedDuration, // 忽略嵌套动画中外层动画的时长设置(只适用于嵌套动画)
    UIViewKeyframeAnimationOptionOverrideInheritedOptions  = UIViewAnimationOptionOverrideInheritedOptions, // 忽略嵌套动画中外层动画的时间函数设置(只适用于嵌套动画)

    // 设置两个关键帧之间的动画模式(可理解为补间动画模式)(只可同时设置一项)
    UIViewKeyframeAnimationOptionCalculationModeLinear     = 0 << 10, // 默认,线性运算模式(匀速补间动画)
    UIViewKeyframeAnimationOptionCalculationModeDiscrete   = 1 << 10, // 离散运算模式(无补间动画)
    UIViewKeyframeAnimationOptionCalculationModePaced      = 2 << 10, // 均匀运算模式
    UIViewKeyframeAnimationOptionCalculationModeCubic      = 3 << 10, // 平滑运算模式
    UIViewKeyframeAnimationOptionCalculationModeCubicPaced = 4 << 10  // 平滑均匀运算模式
}

参考文献

  1. View Programming Guide for iOS
  2. iOS开发之让你的应用动起来

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。

Android自助餐之EventBus框架 - 2016-05-27 14:05:21

Android自助餐之EventBus框架 Android自助餐之EventBus框架 下载完整源代码 配置buildgrandle 实例化EventBus 注册事件监听 定义被传递的事件类 发送事件 处理时间 下载 完整源代码 配置build.grandle compile 'de.greenrobot:eventbus:2.4.0' 实例化EventBus eventBus=EventBus.builder().build(); 注册事件监听 eventBus.register(this); this

IOS高级笔试题 - 2016-05-27 14:05:21

2.Socket在传输过程中用的是什么类型的数据:结构体还是文本?如果是文本,那么怎么进行编码和解码? 参考答案: iOS Socket编程-Objective-C原生API版 iOS Socket编程-Objective-C基于CocoaAsyncSocket版 iOS Socket编程-Swift原生API版 iOS Socket编程-Swift基于AsyncSocket版 3、图片压缩处理问题 参考答案: 关于图片的压缩处理,在ios中常用的方法是先处理像素再处理尺寸。 4、在做图片优化处理的时候,
实现滑动的基本思想是:当触摸View时,系统记下当前触摸点坐标;当手指移动时,系统记下移动后的触摸点坐标,从而获取到相对于前一次坐标点的偏移量,并通过偏移量来修改View的坐标,这样不断重复,从而实现滑动过程. 2.1 Layout方法 在View进行绘制时,会调用onLayout()方法来设置显示的位置 通过修改View的left,top,right,bottom四个属性来控制View的坐标,在每次回调onTouchEvent的时候,获取一下触摸点的坐标: // 视图坐标方式 @Override pub

Linux驱动之输入子系统框架 - 2016-05-27 14:05:20

    好记性不如烂笔头,整理一下笔记~ Linux驱动之输入子系统框架 输入子系统将该类驱动划分为3部分     1、核心层 input.c     2、设备层 Gpio_keys.c ...     3、事件处理层 Evdev.c     事件处理层为纯软件的东西,设备层涉及底层硬件,它们通过核心层建立联系,对外提供open write等接口。 1、我们首先来看,核心层 input.c如何向外界提供接口     在 input_init 中注册了字符设备驱动     register_chrdev(I
主要讲解Android Studio中生成aar文件以及本地方式使用aar文件的方法。 在Android Studio中对一个自己库进行生成操作时将会同时生成*.jar与*.aar文件。 分别存储位置: *.jar:库 /build/intermediates/bundles/debug(release)/classes.jar *.aar:库 /build/outputs/aar/libraryname.aar 两者区别: *.jar: 只包含了class文件与清单文件 ,不包含资源文件,如图片等所有r
本文参考: http://www.cnblogs.com/kenshincui/p/3972100.html#autoid-3-0-0 总结的: 效果: 转场动画 就是从一个场景以动画的形式过渡到另一个场景。转场动画的使用一般分为以下几个步骤:    1.创建转场动画 CATransition    2.设置转场类型transtion.type、子类型transtion.subtype(可选)及其他属性    3.设置转场后的新视图并添加动画到图层    下表列出了常用的转场类型(注意私有API是苹果官方
一、 要实现高德地图定位呢,首先需要做好以下几步准备: 1. 在高德开放平台注册帐号 注册地址: http://lbs.amap.com 2. 在 开发 中下载Android平台下的 地图SDK和定位SDK文件 进入 相关下载 下载自己想要的功能或文件,图只是截取了 地图SDK 的页面, 定位SDK 也是一样,按自己想要的文件下载。下载完成后解压得到: - 3D地图包解压后得到:3D地图显示包“AMap_3DMap_VX.X.X_时间.jar”和库文件夹(包含armeabi、arm64-v8a等库文件)。
背景介绍: Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据(指cookie,html,js,css等文件,这些都可以让你胡乱修改的意思)。 Fiddler 要比其他的网络调试器要更加简单,因为它不仅仅暴露http通讯还提供了一个用户友好的格式。Fiddler是用C#写出来的,它包含一个简单却功能强大的基于JScript.NET事件脚本子系统,它的灵活性非常棒,可以支持众多的http调试任务,并且能够使用
版权声明:本文为博主原创文章,未经博主允许不得转载。 目录 (?) [-] WifiDisplay之P2P的建立 WifiDisplay之RTSP server的创建 这一章中我们来看Wifi Display连接过程的建立,包含P2P的部分和RTSP的部分,首先来大致看一下Wifi Display规范相关的东西。 HIDC: Human Interface Device Class  (遵循HID标准的设备类) UIBC: User Input Back Channel  (UIBC分为两种,一种是Gen

完整Android项目搭建全过程 - 2016-05-27 14:05:46

这篇博客也算是本人从事开发以来的一个总结,以前写博客是为了装逼,现在是为了成长,一个项目如果刚开始的框架没有搭建好,接下来的维护工作将变得异常困难,我们公司的按项目就是因为一开始的框架没有搭建好,只迭代了两个版本便维护不下去了,只能是请高人重新设计的框架,一切重新来过。不同类型的项目对框架的要求自然不同,但是有一点是相同的,那就是,首先对基础语法进行封装,相应工具类、方法样式的封装,前期的封装可以避免后期项目无休止的重构代码,也就不会出现因频繁的改动需求导致代码大量冗余。废话不多说,进入正题,要开始一个移