iOS 动画方法内容整理

Core Animation继承关系图.png

动画在UI交互中是一种增强用户体验的利器,目前看到几乎每一个移动App都会使用到各种动画效果。
在IOS开发中实现动画效果通常有三种方式。

  • 1、基于UIView,为了方便实现简单的动画封装的UIView Animation。
  • 2、基于CALayer的Core Animation框架,这是动画的基础框架。
  • 3、在游戏开发中经常用到的基于物理模拟的动画框架UIKit Dynamics。【未涉及,暂不讲解】
先放置了一个View测试
#define Size(x)  ((x)*[[UIScreen mainScreen] bounds].size.width/375.f)
#define kScreenHeight  [[UIScreen mainScreen] bounds].size.height
#define kScreenWidth  [[UIScreen mainScreen] bounds].size.width
@property (nonatomic, strong) UIView *greenView;

-(void)initCusView{
     self.greenView                  = [[UIView alloc]initWithFrame:CGRectMake(0, Size(0), Size(100), Size(100))];
     self.greenView.backgroundColor  = UIColor.greenColor;
     [self.view addSubview:self.greenView];
}
UIView Animation
UIView Animation 参数说明:
duration : 动画经历时长
delay : 延迟时间,在该延迟时间后才执行动画
options : 系统提供了许多动画执行的方式,比如以下几个
enum {
       //这部分是基础属性的设置
       UIViewAnimationOptionLayoutSubviews            = 1 <<  0,//设置 子视图随父视图展示动画
       UIViewAnimationOptionAllowUserInteraction      = 1 <<  1,//允许在动画执行时用户与其进行交互
       UIViewAnimationOptionBeginFromCurrentState     = 1 <<  2,//允许在动画执行时执行新的动画
       UIViewAnimationOptionRepeat                    = 1 <<  3,//设置动画循环执行
       UIViewAnimationOptionAutoreverse               = 1 <<  4,//设置动画反向执行,必须和重复执行一起使用
       UIViewAnimationOptionOverrideInheritedDuration = 1 <<  5,//强制动画使用内层动画的时间值
       UIViewAnimationOptionOverrideInheritedCurve    = 1 <<  6,//强制动画使用内层动画曲线值
       UIViewAnimationOptionAllowAnimatedContent      = 1 <<  7,//设置动画视图实时刷新
       UIViewAnimationOptionShowHideTransitionViews   = 1 <<  8,//设置视图切换时隐藏,而不是移除
       UIViewAnimationOptionOverrideInheritedOptions  = 1 <<  9,//
       //这部分属性设置动画播放的线性效果
       UIViewAnimationOptionCurveEaseInOut            = 0 << 16,//淡入淡出 首末减速
       UIViewAnimationOptionCurveEaseIn               = 1 << 16,//淡入 初始减速
       UIViewAnimationOptionCurveEaseOut              = 2 << 16,//淡出 末尾减速
       UIViewAnimationOptionCurveLinear               = 3 << 16,//线性 匀速执行   
       //这部分设置UIView切换效果(转场动画使用)
       UIViewAnimationOptionTransitionNone            = 0 << 20,
       UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,//从左边切入
       UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20,//从右边切入
       UIViewAnimationOptionTransitionCurlUp          = 3 << 20,//从上面立体进入
       UIViewAnimationOptionTransitionCurlDown        = 4 << 20,//从下面立体进入
       UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20,//溶解效果
       UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20,//从上面切入
       UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,//从下面切入
  };
animation : UIView动画结束时的状态 ( 比如 : UIView移动到另一点,变成某一种颜色,放大(缩小)后的比例,变化到某一透明度,视图旋转到某一角度)
completion : 动画结束时的回调(这里可以处理一些事件)
usingSpringWithDamping  : 阻尼(弹性系数)
initialSpringVelocity : 初始速率

///基础动画,结束无回调
[UIView animateWithDuration:1 animations:^{
            
}];
///基础动画,结束带回调
[UIView animateWithDuration:1 animations:^{
            
} completion:^(BOOL finished) {

}];
///进阶动画--带动画执行的方式,结束带回调
[UIView animateWithDuration:1 delay:1 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
            
} completion:^(BOOL finished) {

}];

///进阶动画--可设置弹跳效果,结束带回调 带动画执行的方式
[UIView animateWithDuration:1 delay:1 usingSpringWithDamping:0.11 initialSpringVelocity:1 options:UIViewAnimationOptionAutoreverse animations:^{
            
} completion:^(BOOL finished) {
        
}];

可以用来做什么呢:
设置UIView的属性:例如
frame
bounds
center
transform
alpha
backgroundColor
contentStretch

看一下实例:

 self.greenView = [[UIView alloc]initWithFrame:CGRectMake(Size(30), Size(100), Size(100), Size(100))];
 self.greenView.backgroundColor = UIColor.greenColor;
 [self.view addSubview:self.greenView];

///开始动画
-(void)startAnimation{
    
    [UIView animateWithDuration:1 animations:^{
///缩放比例
        self.greenView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.5, 1.5);
///位置调整
        CGRect rect     = self.greenView.frame;
        rect.origin.x += Size(50);
        rect.origin.y += Size(50);
        self.greenView.frame = rect;
///透明度变化
        self.greenView.alpha = 0.5;
///color变化
        self.greenView.backgroundColor = UIColor.redColor;
///圆角改变
        self.greenView.layer.cornerRadius = Size(50);
        self.greenView.clipsToBounds = YES;
    }];
}


我们可以看到self.greenView通过UIView Animation动画将某些属性进行了改变。

现在我们适当的加入一些动画执行的方式【options】

UIViewAnimationOptionRepeat:持续重复动画内容
UIViewAnimationOptionCurveEaseIn:淡入
UIViewAnimationOptionCurveEaseOut:淡出
UIViewAnimationOptionCurveEaseInOut:淡入淡出
UIViewAnimationOptionCurveLinear:匀速运动

[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        CGRect rect  = self.greenView.frame;
        rect.origin.x += Size(275);
        self.greenView.frame = rect;
    } completion:^(BOOL finished) {
}];
动画执行.gif

在开发中可以添加特定的options满足不同的动画需要。

弹簧效果:


弹簧效果.gif
    [UIView animateWithDuration:3 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{
        CGRect rect     = self.greenView.frame;
        rect.origin.x += Size(275);
        self.greenView.frame = rect;
    } completion:^(BOOL finished) {
        
    }];

CABasicAnimation

CABasicAnimation 为layer属性提供了基础的帧动画能力,创建一个CABasicAnimation的实例,使用继承自CAPropertyAnimation的animationWithKeyPath:方法,来指定要添加动画的layer属性的keypath

CABasicAnimation常用的有如下几个属性:
//动画改变属性
@property(nullable, copy) NSString *keyPath;
// 指定执行动画layer的起始值
@property(nullable, strong) id fromValue;
// 指定结束动画layer的结束值
@property(nullable, strong) id toValue;
// 指定执行动画的时候的相对值
@property(nullable, strong) id byValue;

CABasicAnimation 相关常用属性
/* 基础动画开始时刻 CACurrentMediaTime() + 秒数  默认为0.0 这个属性在组动画中很有用 它根据父动画组的持续时间,指定了开始播放动画的时间* /
@property CFTimeInterval beginTime;
/* 基础动画的重复次数 默认为0.0*/
@property float repeatCount;
/*画应该被重复多久。动画会一直重复,直到设定的时间流逝完。它不应该和 repeatCount 一起使用*/
@property CFTimeInterval repeatDuration;
/* 基础动画的时间间隔 默认为0.25*/
@property CFTimeInterval duration;
/* 设置为YES的时候,当动画时间间隔过了后,动画就会移除*/
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;
/* 当动画时间间隔后,指定动画对象的表现。
* 本地时间可能在时间间隔后或者是元素从当前表现层移除后被压缩 
* 合法的值有kCAFillModeForwards、kCAFillModeBackwards、kCAFillModeBoth kCAFillModeRemoved
*/
@property(copy) NSString *fillMode;
//设定动画的速度变化 默认:nil 使用写法:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
@property(nullable, strong) CAMediaTimingFunction *timingFunction;
//动画结束时执行逆动画
@property BOOL autoreverses;
CABasicAnimation的写法。
移动动画

让一个view向左平移,在x方向上从屏幕x中间线型移动到左边消失,耗时1.5秒的动画

移动动画.gif
第一种方法:
// 创建动画  使用动画改变属性 position.x
 CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.x"];
 // 指定基础动画的时间间隔,以秒为单位默认值是 0.25
positionAnima.duration   = 1.5;
//改变前的属性
positionAnima.fromValue  = @((kScreenWidth-Size(100))/2);
//改变后的属性
positionAnima.toValue    = @(-self.greenView.frame.size.width / 2);
//保存最新状态
positionAnima.fillMode   = kCAFillModeForwards;
//动画组件不被移除
positionAnima.removedOnCompletion = NO;
[self.greenView.layer addAnimation:positionAnima forKey:@"position.x"];

第二种方法:
// 创建动画
CABasicAnimation *positionAnima = [CABasicAnimation animation];
// 动画改变属性
positionAnima.keyPath      = @"position";
positionAnima.duration     = 1.5;
// 改变后的属性
positionAnima.toValue = [NSValue valueWithCGPoint:CGPointMake(-Size(50), Size(100+50))];
// 动画组件不被移除
positionAnima.removedOnCompletion = NO;
 // 保存最新状态
positionAnima.fillMode = kCAFillModeForwards;
[self.greenView.layer addAnimation:positionAnima forKey:@"position"];
    
区别:设置的keypath不同第一种方法指定了左移,第二种方法可以是上下左右移动,fromValue、toValue需要根据keypath属性改变
旋转动画
5s完成旋转一周的顺时针旋转的view  并持续旋转

CABasicAnimation * rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue       = [NSNumber numberWithFloat: M_PI * 2.0  ];
rotationAnimation.duration      = 5;
rotationAnimation.cumulative    = YES;
rotationAnimation.repeatCount   = HUGE_VAL;
rotationAnimation.removedOnCompletion = NO;
[self.greenView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
缩放动画
/* 放大缩小 */
 
// 设定为缩放
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
// 动画选项设定
animation.duration = 2.5; // 动画持续时间
animation.repeatCount = 1; // 重复次数
animation.autoreverses = YES; // 动画结束时执行逆动画
// 缩放倍数
animation.fromValue = [NSNumber numberWithFloat:1.0]; // 开始时的倍率
animation.toValue     = [NSNumber numberWithFloat:2.0]; // 结束时的倍率
// 添加动画
[self.greenView.layer addAnimation:animation forKey:@"scale-layer"];

KeyPath的改变动画的效果就不一样,开发中改变KeyPath的属性可以实现大多数我们需要的动画执行的效果

animationWithKeyPath值类型:
position      =   路径(一般用CAKeyframeAnimation)
transform.rotation     = 旋转
transform.rotation.x  = x旋转
transform.rotation.y  = y旋转
transform.rotation.z  = z旋转(顺逆时针)
transform.translation = 平移
transform.translation.x = x平移
transform.translation.y = y平移
transform.scale             = 比例转换
transform.scale.x = x的比例转换
transform.scale.y = y的比例转换
transform.scale.y = y的比例转换
transform.rotation.z = 平面圆的转换
opacity = 透明度
margin
zPosition
backgroundColor = 背景颜色
cornerRadius = 圆角
borderWidth
bounds
contents
contentsRect
cornerRadius
frame  坐标
hidden  隐藏
mask
masksToBounds
shadowColor
shadowOffset
shadowOpacity
shadowRadius
大家可以尝试使用不同的keypath看看动画效果

用CABasicAnimation执行动画,在动画结束后会回归动画开始前的状态。想要解决的话,必须设置“removedOnCompletion”和“fillMode”这两个属性。

// 动画终了后不返回初始状态
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
但是

由于在开发过程中光是CABasicAnimation的fromValue、toValue起点和终点设置是无法满足我们希望在动画中途进行更多的变化的需求,所以我们需要认识一下CAKeyframeAnimation

CAKeyframeAnimation[关键帧动画]

从上面的继承图我们看出CAKeyframeAnimation 比CABasicAnimation多了更多的可设置属性

/* 提供关键帧数据的数组,数组中的每一个值都对应一个关键帧。根据动画类型(keyPath)的不同 ,
值的类型不同*/
@property(nullable, copy) NSArray *values;
/*基于点的属性的路径,即动画属性类型为CGPoint。如: position、anchorPoint、transform.translation等
如果为此属性指定非空值,则会忽略values属性*/
@property(nullable) CGPathRef path;
/* keyTimes的值与values中的值一一对应指定关键帧在动画中的时间点,取值范围为[0,1]。当keyTimes没有设置的时候,
各个关键帧的时间是平分的*/
@property(nullable, copy) NSArray*keyTimes;
/*指定每个关键帧之间的动画缓冲效果,timingFunctions.count = keyTimes.count-1*/
@property(nullable, copy) NSArray*timingFunctions;
/*关键帧间插值计算模式*/
@property(copy) NSString *calculationMode;
/*针对cubic 计算模式的动画,这些属性提供对插值方案的控制。每个*关键帧都可以具有与之相关的
张力、连续性和偏差值,这些值的范围在[-1,1]内(这定义了Kochanek-*Bartels样条,见http://en.wikipedia.org/wiki/Kochanek-Bartels_spline)。

*tensionValues控制曲线的“紧密度”(正值更紧,负值更圆)。
*continuityValues控制段的连接方式(正值表示锐角,负值表示倒角)。
*biasValues定义曲线发生的位置(正值在控制点之前移动曲线,负值在控制点之后移动它)。

*每个数组中的第一个值定义第一个控制点的切线的行为,第二个值控*制第二个点的切线,依此类推。任何未指定的值都默认为零
 *(如果未指定,则给出Catmull-Rom样条曲线)。 
*/
@property(nullable, copy) NSArray*tensionValues;
@property(nullable, copy) NSArray*continuityValues;
@property(nullable, copy) NSArray *biasValues;

/*定义沿路径动画的对象是否旋转以匹配路径切线*/
@property(nullable, copy) NSString *rotationMode;

关键帧动画其实通过一组动画类型的值(或者一个指定的路径)和这些值对应的时间节点以及各时间节点的过渡方式来控制显示的动画。关键帧动画可以通过path属性和values属性来设置动画的关键帧。

通过path设置动画
关键帧-path.gif
绕线一周动画
CGMutablePathRef path = CGPathCreateMutable();
//第一个关键帧  -100,-100
CGPathMoveToPoint(path, NULL, 0, 0);
//第二个关键帧  100,-100
CGPathAddLineToPoint(path, NULL, 100, 0);
//第三个关键帧  100,100
 CGPathAddLineToPoint(path, NULL, 100, 100);
//第四个关键帧  -100,100
CGPathAddLineToPoint(path, NULL, 0, 100);
//第五个关键帧  -100,-100
CGPathAddLineToPoint(path, NULL, 0, 0);
     
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"transform.translation";
animation.path = path;
animation.duration = 4;
animation.keyTimes = @[@(0),@(0.1),@(0.5),@(0.75),@(1)];
animation.timingFunctions = @[[CAMediaTimingFunction functionWithControlPoints:1 :0.5 :0.5 :0.5],
                                      [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                      [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
                                      [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
    //动画结束后保持动画最后的状态,两个属性需配合使用
 animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
        
CGPathRelease(path);
[self.greenView.layer addAnimation:animation forKey:@""];
通过values设置动画
关键帧-values.gif
 /// 放大缩小放大缩小【隐藏】
CAKeyframeAnimation* animation  = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
animation.duration              = 2.0;// 动画时间
animation.removedOnCompletion    = NO;
animation.values                = @[@1,@1.2,@1,@1.2,@0];
//动画结束后保持动画最后的状态,两个属性需配合使用
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[self.greenView.layer addAnimation:animation forKey:nil];

CAAnimationGroup[动画组]

可以保存一组动画CAKeyframeAnimation、CABasicAnimation对象,将CAAnimationGroup对象加入图层后,组中所有动画对象可以同时并发运行。

购物车动画
动画组.gif
 /* 动画1(在XY轴方向移动) */
CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:@"transform.translation"];
// 终点设定
animation1.toValue = [NSValue valueWithCGPoint:CGPointMake(kScreenWidth-Size(50), kScreenHeight-Size(50))]; // 終点

/* 动画2(绕Z轴中心旋转) */
CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
// 设定旋转角度
animation2.fromValue = [NSNumber numberWithFloat:0.0]; // 开始时的角度
animation2.toValue = [NSNumber numberWithFloat:4 * M_PI]; // 结束时的角度
     
 /* 动画组 */
 CAAnimationGroup *group = [CAAnimationGroup animation];
// 动画选项设定
group.duration = 3.0;
group.repeatCount = 1;
// 添加动画
group.animations = [NSArray arrayWithObjects:animation1, animation2, nil];
[self.greenView.layer addAnimation:group forKey:@"move-rotate-layer"];

注意:默认情况下,一组动画对象是同时运行的,也可以通过设置单个动画对象的beginTime属性来更改动画的开始时间,单个动画的执行效果可以与动画组执行效果属性分开设定,根据需要调整改变。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容