动画在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) {
}];
在开发中可以添加特定的options满足不同的动画需要。
弹簧效果:
[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秒的动画
第一种方法:
// 创建动画 使用动画改变属性 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设置动画
绕线一周动画
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设置动画
/// 放大缩小放大缩小【隐藏】
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对象加入图层后,组中所有动画对象可以同时并发运行。
购物车动画
/* 动画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属性来更改动画的开始时间,单个动画的执行效果可以与动画组执行效果属性分开设定,根据需要调整改变。