1> UIImageView GIF 动画
GIF图的原理是:获取图片,存储在图片数组中,按照图片数组的顺序将图片以一定的速度播放
UIImageView *showGifimageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100, 300, 300)]; [self.view addSubview:showGifimageView]; // 创建一个存储图片的数组 NSMutableArray *saveImageArray = [NSMutableArray array]; // 获取图片 for (int i = 1; i < 12; i++) { // 拼接图片名 NSString *imageName = [NSString stringWithFormat:@"%d.tiff", i]; // 根据图片名获取图片 UIImage *image = [UIImage imageNamed:imageName]; // 将图片加到数组 [saveImageArray addObject:image]; } // 设置gif的图片组 showGifimageView.animationImages = saveImageArray; // 设置播放速率 showGifimageView.animationDuration = 1; // 设置播放的次数 showGifimageView.animationRepeatCount = 0; // 开始动画 [showGifimageView startAnimating];
在APP中,加载界面的时候我们都会看到一个想风火轮的动画在不停的转,这个动画其实是iOS中的一个类 UIActivityIndicatorView
// 加载旋转的菊花效果 // 无需设置frame UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; // 确定位置 indicatorView.center = self.view.center; [self.view addSubview:indicatorView]; // 将菊花动画效果开启 [indicatorView startAnimating];
传达状态
提高用户对直接操作的感知
帮助用户可视化操作的结果
谨慎添加动画,尤其是在那些不能提供沉浸式用户体验(让人专注在当前由设计者营造的情境下感到愉悦和满足暂时忘记真实世界的情境)的App中。
如果App主要关注一些严肃的任务或者生产性任务,那么动画就显得多余了,还会无端打乱App的使用流程,降低应用的性能,让用户从当前的任务中分心。
用户习惯于内置iOS App使用的精细动画。事实上,用户趋向于把视图之间的平滑转换,对设备方向改变的流畅响应和基于物理力学的滚动效果看作是iOS体验的一部分。除非你的应用能够给用户沉浸式的体验—比如游戏(自定义动画应该可以与内置应用的动画相媲美)
在App中使用风格类型一致的动画非常重要,可以让用户构建基于使用App获得的用户体验。
UIKit 直接将动画集成到 UIView 类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持。
执行动画的工作由 UIView 类自动完成,但仍希望在执行动画时通知视图,为此需要将改变属性的代码放在 [UIView beginAnimations:nil context:nil] 和 [UIView commitAnimations] 之间。
UIView位置大小动画(改变View的frame)
UIView颜色动画(改变View的color)
UIView透明度动画(改变View的alpha)
仿射-翻转rotation
仿射-旋转transform
+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;
参数animationID: 是一个标识符字符串,用于告诉系统要进行哪一个动画,可以自由定义
参数context:额外的上下文信息传递,没有就置为 nil
+ (void)setAnimationDuration:(NSTimeInterval)duration;
参数duration:表示动画持续的时间长度,系统默认为0.2s,具体值可以根据需求自行设置
+ (void)setAnimationDelegate:(nullable id)delegate;
参数delegate:设置代理变量,一般为nil,代理主要用于监听动画的开始和结束
主要是对frame、color、alpha、翻转方向和旋转角度的操作
+ (void)commitAnimations;
该方法主要是提交动画,也就是告诉系统动画执行完成
// UIView动画有开始beginAnimation,有结束commitAnimations // 第一步:开始UIView动画 [UIView beginAnimations:@"mov" context:nil]; // 第二步:设置动画时常 [UIView setAnimationDuration:3]; // 第三步:设置UIView动画的回调代理 [UIView setAnimationDelegate:self]; // 第四步:设置相关的对象的frame _showView.frame = CGRectMake(100, 100, 200, 100); // 第五步:提交动画效果 [UIView commitAnimations];
[UIView beginAnimations:@"color" context:nil]; [UIView setAnimationDuration:4]; [UIView setAnimationDelegate:self]; _showView.backgroundColor = [UIColor purpleColor]; [UIView commitAnimations];
[UIView beginAnimations:@"alpha" context:nil]; [UIView setAnimationDuration:5]; [UIView setAnimationDelegate:self]; _showView.alpha = 0.1; [UIView commitAnimations];
// 第一步:开始UIView动画 [UIView beginAnimations:@"rotation" context:nil]; // 第二步:设置动画时常 [UIView setAnimationDuration:0.5]; // 第2.5步:设置淡入的效果(感觉效果不明显) [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; // 第三步:设置UIView动画的回调代理 [UIView setAnimationDelegate:self]; // 第4步:设置翻转的方式 [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:_showView cache:YES]; [UIView commitAnimations];
[UIView beginAnimations:@"transform" context:nil]; [UIView setAnimationDuration:2]; [UIView setAnimationDelegate:self]; // 第4步:设置旋转角度 CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI_2); // 第4.5步:设置旋转角度的对象 [_showView setTransform:transform]; [UIView commitAnimations];
+ (void)setAnimationWillStartSelector:(nullable SEL)selector; + (void)setAnimationDidStopSelector:(nullable SEL)selector;
这两个方法默认是 nil,不能使用,一般使用它们的替代方法
- (void)animationWillStart:(NSString *)animationID context:(void *)context; - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;
实际操作的代码
#pragma mark - UIViewAnimationDelegate的协议方法 - (void)animationWillStart:(NSString *)animationID context:(void *)context { NSLog(@"%s__%d--ID = %@, context = %@", __FUNCTION__, __LINE__, animationID, context); } - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { NSLog(@"%s__%d--ID = %@, context = %@", __FUNCTION__, __LINE__, animationID, context); }
注:UIViewAnimationDelegate 代理不需要我们在遵循,系统已经封装好了
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
参数duration:设置动画时长
参数animations:是一个Block,主要用于设置动画
参数completion:也是一个Block,主要用于设置在动画结束后的一些操作
__weak typeof(self)weakSelf = self; // 第1个参数:设置动画时长 // 第2个参数:设置动画 // 第3个参数:动画完成时进行的事情 [UIView animateWithDuration:2 animations:^{ weakSelf.playImageView.frame = CGRectMake(67, 74, 240, 286); } completion:^(BOOL finished) { NSLog(@"finished"); }];
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
参数delay:动画执行的延迟时间
参数options:枚举值动画效果,有很多枚举值,大家可以根据需要进行选取
参数animations:是一个Block,主要用于设置动画
参数completion:也是一个Block,主要用于设置在动画结束后的一些操作
// 第1个参数:设置动画时长 // 第2个参数:动画的延迟时间 // 第3个参数:枚举值动画效果 // 第4个参数:设置动画 // 第5个参数:动画完成时进行的事情 __weak typeof(self)weakSelf = self; [UIView animateWithDuration:2 delay:1 options:UIViewAnimationOptionOverrideInheritedOptions animations:^{ weakSelf.playImageView.center = weakSelf.view.center; } completion:^(BOOL finished) { NSLog(@"finished"); }];
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion
参数delay:动画执行的延迟时间
参数options:关键帧枚举值动画效果,有很多枚举值,大家可以根据需要进行选取
参数animations:是一个Block,主要用于设置动画。在这里需要添加一个方法,即创建Block的关键帧
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations;
参数frameStartTime:帧动画的开始时间
参数frameDuration:帧动画的持续时间
参数animations:Block,用于设置动画
// 第1个参数:设置动画时长 // 第2个参数:动画的延迟时间 // 第3个参数:关键帧枚举值动画效果 // 第4个参数:开始动画 // 第5个参数:动画完成时进行的事情 __weak typeof(self)weakSelf = self; [UIView animateKeyframesWithDuration:2 delay:1 options:UIViewKeyframeAnimationOptionAllowUserInteraction animations:^{ // 在这里需要添加一个方法,即创建Block的关键帧 // 帧动画的开始时间 // 帧动画的持续时间 [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.5 animations:^{ weakSelf.playImageView.center = weakSelf.view.center; }]; } completion:^(BOOL finished) { NSLog(@"finished"); }];
Spring Animation 是一种特殊的动画曲线,自从 iOS 7 开始被广泛应用在系统动画中。
事实上,从 iOS 7 起几乎所有的系统动画都用的是 Spring Animation,包括 App 文件夹打开/关闭效果、键盘弹出效果、UISwitch 控件的开关效果、不同 View Controller 之间的 Push 动画、Modal 出现和消失的动画、Siri 的出现和消失动画,等等
+ (void)animateWithDuration:(NSTimeInterval)duration //动画时长参数 delay:(NSTimeInterval)delay //动画延迟参数 usingSpringWithDamping:(CGFloat)dampingRatio //动画阻尼参数,0.0~1.0,越小动画越明显 initialSpringVelocity:(CGFloat)velocity //动画初始变化速率 options:(UIViewAnimationOptions)options //动画可选参数 animations:(void (^)(void))animations //动画最终效果代码块 completion:(void (^)(BOOL finished))completion //动画播放完成后执行的代码块
Spring Animation 是线性动画或 ease-out 动画的理想替代品。由于 iOS 本身大量使用的就是 Spring Animation,用户已经习惯了这种动画效果,因此使用它能使 App 让人感觉更加自然,用 Apple 的话说就是「instantly familiar」。此外,Spring Animation 不只能针对位置变化使用,它适用于所有可被添加动画效果的属性。
__weak typeof(self)weakSelf = self; [UIView animateWithDuration:3.0 // 动画时长 delay:0.0 // 动画延迟 usingSpringWithDamping:1.0 // 类似弹簧振动效果 0~1 initialSpringVelocity:15.0 // 初始速度 options:UIViewAnimationOptionCurveEaseInOut // 动画过渡效果 animations:^{ CGPoint point = _imageView.center; point.y += 150; [_imageView setCenter:point]; } completion:^(BOOL finished) { // 动画完成后执行 NSLog(@"finished"); }];
CoreAnimation 动画位于 iOS 框架的 Media 层
CoreAnimation 动画实现需要添加 QuartzCore.Framework
CoreAnimation 基本上是 Layer Animation
CALayer 负责绘制,提供 UIView 需要展示的内容,不能交互。
UIView 负责交互,显示 CALayer 绘制的内容。
UIView 是 iOS 系统中界面元素的基础,所有的界面元素都是继承自它。它本身完全是由 CoreAnimation 来实现的。它真正的绘图部分,是由一个 CALayer 类来管理。UIView 本身更像是一个 CALayer 的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等,实际上内部都是在访问它所包含的 CALayer 的相关属性。
UIView 有个重要属性 layer ,可以返回它的主 CALayer 实例。
UIView 的 CALayer 类似 UIView 的 子View树形结构,也可以向它的 layer 上添加 子layer,来完成某些特殊的表示。即 CALayer 层是可以嵌套的。
CALayer(层) 是屏幕上的一个矩形区域,在每一个 UIView 中都包含一个 根CALayer,在 UIView 上的所有视觉效果都是在这个 Layer 上进行的。
CALayer 外形特征主要包括:
层的大小尺寸
背景色
内容(可以填充图片或者使用Core Graphics绘制的内容)
矩形是否使用圆角
矩形是否有阴影
Layer 有很多种,最常用也是最基本的是 CALayer,当然还包括其他的子类:
CAScrollerLayer 简化显示层的一部分
CATextLayer 文本层
CAGradientLayer、CAShapeLayer等等
代码实例:
// 设置圆角 self.xqImageView.layer.cornerRadius = self.xqImageView.frame.size.width / 2; // 影响阴影效果(注意:masksToBounds这个属性影响layer层的阴影效果,导致阴影不显示) // self.xqImageView.layer.masksToBounds = YES; // 设置layer的阴影颜色 self.xqImageView.layer.shadowColor = [UIColor lightGrayColor].CGColor; // 设置layer的阴影的透明度 self.xqImageView.layer.shadowOpacity = 0.5; // 设置layer的阴影的偏移量 self.xqImageView.layer.shadowOffset = CGSizeMake(20, 10); // 设置layer的阴影的 self.xqImageView.layer.shadowRadius = 1;
渲染:当更新层改变不能立即显示在屏幕上,当所有的层都准备好时,可以调用 setNeedsDisplay 方法来重绘显示。
坐标系统:CALayer 的坐标系统比 UIView 多了一个 anchorPoint(锚点) 属性,使用 CGPoint 结构表示,值域是0~1,是个比例值。这个点是各种图形变换的坐标原点,同时会更改 layer 的 position 的位置,它的缺省值是{0.5,0.5},即在 layer 的中央,如下图。
动画的运作:对UIView的subLayer(非主Layer)属性进行更改,系统将自动进行动画生成,动画持续时间的缺省值似乎是0.5秒。
变换:要在一个层中添加一个3D或仿射变换,可以分别设置层的transform或affineTransform属性。
变形:Quartz Core的渲染能力,使二维图像可以被自由操纵,就好像是三维的。图像可以在一个三维坐标系中以任意角度被旋转,缩放和倾斜。CATransform3D的一套方法提供了一些魔术般的变换效果。
隐式动画:无需指定任何动画的类型,仅仅改变一个属性,然后Core Animation来决定如何及何时去做动画。
显式动画:对一些属性做指定的自定义动画,或者创建非线性动画,比如沿着任意一条曲线移动。
与UIView动画比,CoreAnimation能够实现更多复杂、好看、高效的动画效果
阴影,圆角,带颜色的边框
3D变换
透明遮罩
多级非线性动画
CABasicAnimation 基本单一类型的动画
CAKeyframeAnimation 帧动画,主要操作属性有 keyPath 和 values 值组合
CAAnimationGroup 组合动画,操作属性:animations 将CAAnimation类型的动画加入数组,FIFO队列的方式执行
+ (instancetype)animation;
@property(nullable, copy) NSString *keyPath;
keyPath:动画轨迹,告诉layer层需要执行什么样的动画。类型为 NSString * 但是值是系统设置好的,不能改变,改变后动画将失去效果。例如 "position":路径动画;"transform":旋转效果;"contents":改变内容等。
根据keyPath的值进行相应的操作
@property CFTimeInterval duration;
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
参数anim:动画
参数key:一个自定义的key,可以根据这个key值来删除动画
- (void)removeAnimationForKey:(NSString *)key; // 根据key移除动画
// 第一步:创建动画对象 CABasicAnimation *basicAnimation = [CABasicAnimation animation]; // 第二步:设置动画轨迹,告诉layer层需要执行什么样的动画,设置的内容为CALayer的相关属性 basicAnimation.keyPath = @"position"; // 第三步:设置初始位置和最终位置 basicAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)]; basicAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 200)]; // 第四步:如果要设置动画完成后不回到初始状态,需要实现以下两句代码 basicAnimation.removedOnCompletion = NO; // 设置保存动画状态 basicAnimation.fillMode = kCAFillModeForwards; // 第四步:设置动画时长 basicAnimation.duration = 6.0f; // 第五步:将BasicAnimation动画添加到CALayer上 [self.testView.layer addAnimation:basicAnimation forKey:@"basic"];
CABasicAnimation *basic = [CABasicAnimation animation]; basic.keyPath = @"transform"; // 参数1:value值:角度 最大旋转180°,就是会按照你设定的角度得到的效果的最短距离去旋转,如果是360°的倍数就静止不动 // 参数2:x 沿x轴旋转 纵向翻转 // 参数3:y 沿y轴旋转 横向翻转 // 参数4:z 沿z轴旋转 平面旋转 basic.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)]; basic.duration = 2.0; [self.testView.layer addAnimation:basic forKey:@"transform"];
CABasicAnimation *contents = [CABasicAnimation animation]; contents.keyPath = @"contents"; contents.toValue = (id)[UIImage imageNamed:@"2.jpg"].CGImage; contents.duration = 1.0f; contents.repeatCount = MAXFLOAT; // 动画无限循环 contents.delegate = self; [self.testView.layer addAnimation:contents forKey:@"contents"];
// 第1步:创建动画对象 CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animation]; // 第2步:设置动画轨迹 keyFrameAnimation.keyPath = @"transform.rotation"; // 第3步:设置旋转的角度(弧度的计算公式:度数 / 180 * M_PI) keyFrameAnimation.values = @[@(0 / 180.0 * M_PI), @(90 / 180.0 * M_PI), @(180 / 180.0 * M_PI), @(270 / 180.0 * M_PI), @(360 / 180.0 * M_PI)]; // 第4步:设置动画的时长 keyFrameAnimation.duration = 3; keyFrameAnimation.repeatCount = MAXFLOAT; // 动画无限循环 [self.xqImageView.layer addAnimation:keyFrameAnimation forKey:@"keyFrameAnimation"];
// 平移动画 CABasicAnimation *basic1 = [CABasicAnimation animation]; basic1.keyPath = @"transform.translation.y"; basic1.toValue = @(400); // 缩放 CABasicAnimation *basic2 = [CABasicAnimation animation]; basic2.keyPath = @"transform.scale"; basic2.toValue = @(0.5); // 旋转动画 CABasicAnimation *basic3 = [CABasicAnimation animation]; basic3.keyPath = @"transform.rotation"; basic3.toValue = @(2 * M_PI); // 创建管理各个动画的动画组 CAAnimationGroup *group = [CAAnimationGroup animation]; group.animations = @[basic1, basic2, basic3]; group.duration = 3; [_xqImageView.layer addAnimation:group forKey:@"group"];
CASpringAnimation *spring = [CASpringAnimation animation]; spring.keyPath = @"transform.scale"; spring.fromValue = @1; spring.toValue = @0.25; spring.duration = 3; [_xqImageView.layer addAnimation:spring forKey:@"spring"];