iOS动画
好的应用都有一个共同的特点,那就是良好的用户体验,动画作为提升用户体验一个重要技术,在开发人员和产品设计人员手里,则应该受到足够重视。
CoreGraphics:
它是ios的核心图形库,平时使用最频繁的point,size,rect等这些图形,都定义在这个框架中,类名以CG开头的都属于CoreGraphics框架,它提供的都是C语言的函数接口,是可以在ios和mac os通用的。
QuartzCore:
这个框架的名称感觉不是很清晰,不明确,但是看它的头文件可以发现,它其实就是CoreAnimation,这个框架的头文件只包含了CoreAnimation.h
Core animation(核心动画)
锚点anchorPoint
- 概念
- anchorPoint是相对于自身layer,anchorPoint点(锚点)的值是用相对bounds的比例值来确定的。
- anchorPoint相当于支点,可以用作旋转变化、平移、缩放,如果修改anchorPoint则layer的frame会发生改变,position不会发生改变.
- 修改position与anchorPoint中任何一个属性都不影响另一个属性.
- 单位:
- 0~1
- anchorPoint和position的关系
- anchorPoint和position不是一个点
- anchorPoint和position和frame的三条关系
- 第一条关系说明:锚点不变时,frame改变,position会随着变化
- 第二条关系说明:锚点不变时,position变化,frame会随着变化
- 第三条关系说明:锚点改变, position不影响,frame会随着变化
创建动画的三个步骤
- 创建核心动画大致分为三个步骤:
- 初始化动画对象
- 设置需要修改动画的属性值
- 把动画添加到layer上
隐式动画和显示动画
当动画完成以后你看到的都是假象,你的view并没有变化,layer分为两层:
- presentationlayer 呈现成
- 凡是眼睛看到的都是呈现层
- modelayer 模型层
- 用来存储数据,等到屏幕需要刷新的时候绘制到呈现层
显示动画
- 通过自定义三步骤的动画都是显示动画
隐式动画
- 通过自创建的layer,修改其属性会默认添加一些动画效果,这些动画效果就是隐式动画
- CALayer 默认时间 0.25s(位置、颜色、大小)
- 必须是独立的CALayer才有隐式动画, UIView的根layer是没有的
例如代码如下:
_layer = [CALayer layer];
_layer.frame = CGRectMake(200, 100, 100, 100);
//可移值性
_layer.backgroundColor = [UIColor yellowColor].CGColor;
[self.view.layer addSublayer:_layer];
_layer.frame = CGRectMake(200, 400, 100, 100);
iOS动画框架结构
在iOS系统中给我们提供了基于Core animation
这几种动画,如下图所示:
CABasicAnimation(基本动画)
CABasicAnimation的属性
属性 | 说明 |
---|---|
duration | 动画的时长 |
repeatCount | 重复的次数 |
repeatDuration | 设置动画的时间,在改时间内一直执行,不计算次数 |
beginTime | 指定动画开始时间 |
timingFunction | 设置动画的速度变化 |
autoreverses | 动画结束是否执行逆动画 |
fromValue | 属性的起始值 |
toValue | 结束的值 |
byValue | 改变属性相同起始值的改变量 |
CABasicAnimation的Keypath属性
apple官网动画属性
如下常用的属性:
Keypath | 说明 | 使用 |
---|---|---|
transform.scale | 比例转化 | @(cgfloat) |
transform.scale.x | 宽的比例 | @(cgfloat) |
transform.scale.y | 高的比例 | @(cgfloat) |
transform.rotation.x | 围绕x轴旋转 | @(M_PI) |
transform.rotation.y | 围绕y轴旋转 | @(M_PI) |
transform.rotation.z | 围绕z轴旋转 | @(M_PI) |
cornerRadius | 圆角的设置 | @(50) |
backgroundColor | 背景颜色的变化 | (id)[uicolor redcolor].cgcolor |
bounds | 大小,中心不变 | [nsvalue valuewithcgrect:] |
position | 位置(中心点的改变) | [nsvalue valuewithcgpoint] |
contents | 内容(比如图片) | (id)image.cgimage |
opacity | 透明度 | @(cgfloat) |
contentsRect.size.width | 横向拉伸缩放(需要设置contents) | @(cgfloat) |
CABasicAnimation的例子
做一个从上往下走的动画
CABasicAnimation *basicani = [CABasicAnimation animationWithKeyPath:@"position.y"];//中心点
basicani.toValue = @400;
basicani.duration = 2;
//动画完成不移除状态
basicani.removedOnCompletion = NO;
//保持最后的状态
basicani.fillMode = kCAFillModeForwards;
basicani.delegate = self;
[_RedView.layer addAnimation:basicani forKey:@""];
CAKeyframeAnimation关键帧动画
CAKeyframeAnimation设置动画的路径分为两种
- 设置path
- 设置一组的values
CAKeyframeAnimation的例子
代码如下:设置了一个飞机按照轨迹飞行的动画
//贝塞尔曲线
/*
想要通过程序写曲线或者不规则的图形时,是很复杂的
贝塞尔曲线就是通过代码转换成公式来进行绘制的。
起点
终点
2个控制点
**/
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 200)];
[path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(100, 100) controlPoint2:CGPointMake(200, 300)];
CALayer *planelayer = [CALayer layer];
planelayer.backgroundColor = [UIColor redColor].CGColor;
planelayer.frame = CGRectMake(0, 0, 40, 40);
planelayer.anchorPoint = CGPointMake(0.5, 0.8);
planelayer.contents = (id)[UIImage imageNamed:@"feiji"].CGImage;
[self.view.layer addSublayer:planelayer];
//需要添加到layer上
//形状图 矢量图 这个事layer的子类,CAShapeLayer有一个硬件加速更快
CAShapeLayer *shapelayer = [CAShapeLayer layer];
shapelayer.path = path.CGPath;
shapelayer.fillColor = nil; //填充颜色
shapelayer.strokeColor = [UIColor blueColor].CGColor;//画笔的颜色
[self.view.layer addSublayer:shapelayer];
/*
CAKeyframeAnimation 设置动画的路径分为两种:
1. 设置path
2. 设置一组values
***/
//飞行动画
CAKeyframeAnimation *planeAni = [CAKeyframeAnimation animation];
planeAni.keyPath = @"position";
planeAni.path = path.CGPath;
planeAni.rotationMode = kCAAnimationRotateAuto;
planeAni.duration = 3.5;
planeAni.fillMode = kCAFillModeForwards;
planeAni.removedOnCompletion = NO;
[planelayer addAnimation:planeAni forKey:@"planeAni"];
CAAnimationGroup动画组
将多个动画合并到一起的动画就是CAAnimationGroup动画组
CAAnimationGroup例子
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 200)];
[path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(100, 100) controlPoint2:CGPointMake(200, 300)];
CALayer *colorlayer = [CALayer layer];
colorlayer.frame = CGRectMake(0, 0, 60, 60);
colorlayer.position = CGPointMake(50, 200);
colorlayer.backgroundColor = [UIColor yellowColor].CGColor;
[self.view.layer addSublayer:colorlayer];
//飞行动画
CAKeyframeAnimation *planeAni = [CAKeyframeAnimation animation];
planeAni.keyPath = @"position";
planeAni.path = path.CGPath;
planeAni.rotationMode = kCAAnimationRotateAuto;
//改变大小
CABasicAnimation *sizeanim = [CABasicAnimation animation];
sizeanim.keyPath = @"transform.scale";
sizeanim.toValue = @0.5;
//改变颜色
CGFloat redcolor = arc4random() % 255 / 255.0f;
CGFloat greencolor = arc4random() % 255 / 255.0f;
CGFloat bluecolor = arc4random() % 255 / 255.0f ;
UIColor *color = [UIColor colorWithRed:redcolor green:greencolor blue:bluecolor alpha:1];
CABasicAnimation *colorAnim = [CABasicAnimation animation];
colorAnim.keyPath = @"backgroundColor";
colorAnim.toValue = (id)color.CGColor;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = @[planeAni, sizeanim, colorAnim];
group.duration = 4.0f;
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[colorlayer addAnimation:group forKey:nil];
转场动画
CATransition转场动画
CATransition是CAAnimation的子类,用于过渡动画或转场动画。为视图层移入移除屏幕提供转场动画。
简单的转场动画ImageView的切换:
@property (weak, nonatomic) IBOutlet UIImageView *ImageView;
@property (strong,nonatomic)NSArray *images;
@property (assign,nonatomic)NSInteger index;
- (void)Createtransition{
if(_index == 3){
_index = 0;
}
_index ++ ;
NSString *imagename = _images[_index];
_ImageView.image = [UIImage imageNamed:imagename];
CATransition *anima = [CATransition animation];
anima.type = @"cube";
[_ImageView.layer addAnimation:anima forKey:@"CATransitionkey"];
}
_images = @[@"1.jpg",@"2.jpg",@"3.jpg",@"4.jpg"];
_index = 0;
[self Createtransition];
转场动画的重要属性
-
type:系统自带转场动画的类型
- 官方自定
CA_EXTERN NSString * const kCATransitionFade; CA_EXTERN NSString * const kCATransitionMoveIn; CA_EXTERN NSString * const kCATransitionPush; CA_EXTERN NSString * const kCATransitionReveal;
- 私有的type:
NSString *const kCATransitionCube = @"cube"; NSString *const kCATransitionSuckEffect = @"suckEffect"; NSString *const kCATransitionOglFlip = @"oglFlip"; NSString *const kCATransitionRippleEffect = @"rippleEffect"; NSString *const kCATransitionPageCurl = @"pageCurl"; NSString *const kCATransitionPageUnCurl = @"pageUnCurl"; NSString *const kCATransitionCameraIrisHollowOpen = @"cameraIrisHollowOpen"; NSString *const kCATransitionCameraIrisHollowClose = @"cameraIrisHollowClose";
subtype:动画类型的方向
CA_EXTERN NSString * const kCATransitionFromRight;
CA_EXTERN NSString * const kCATransitionFromLeft;
CA_EXTERN NSString * const kCATransitionFromTop;
CA_EXTERN NSString * const kCATransitionFromBottom;
transitionFromViewController
转场动画的type
UIViewAnimationOptionTransitionNone = 0 << 20, // default
UIViewAnimationOptionTransitionFlipFromLeft = 1 << 20,
UIViewAnimationOptionTransitionFlipFromRight = 2 << 20,
UIViewAnimationOptionTransitionCurlUp = 3 << 20,
UIViewAnimationOptionTransitionCurlDown = 4 << 20,
UIViewAnimationOptionTransitionCrossDissolve = 5 << 20,
UIViewAnimationOptionTransitionFlipFromTop = 6 << 20,
UIViewAnimationOptionTransitionFlipFromBottom = 7 << 20,
AViewController *a = self.childViewControllers[0];
BViewController *b = self.childViewControllers[1];
CViewController *c = self.childViewControllers[2];
[self transitionFromViewController:_currentViewController
toViewController:b
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
} completion:^(BOOL finished) {
}];
Transition Animation(自定义转场动画)
第一种:presented& Dismiss
需要实现UIViewController
中的UIViewControllerTransitioningDelegate
协议,实现下面两个API:
//presented会调用的方法
- (nullable id ) v:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//Dismiss会调用的方法
- (nullable id )animationControllerForDismissedController:(UIViewController *)dismissed
第二种:Push&Pop
需要实现navigationController
中的UINavigationControllerDelegate
协议,实现下面一个API:
- (nullable id )navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC{
上述API的返回值id
就是我们需要自定义的动画
-
自定义转场动画步骤:
创建一个
NSObject
的对象核心:遵守
-
实现下面2个方法:
//动画持续时间,单位是秒 - (NSTimeInterval)transitionDuration:(id
)transitionContext { } //动画效果 - (void)animateTransition:(id )transitionContext { } 在
animateTransition
方法中实现你的动画动画结束后调用
completeTransition
告诉系统转场动画结束
-
自定义转场动画的核心思想
- containerView 动画的容器
- ToVC
- FromVC
- mask
- completeTransition动画完成
Push&Pop形式转场动画
-
自定义转场动画入口:
在Push和Pop的
ViewController
遵守UINavigationControllerDelegate
协议-
实现下面方法,并返回自定义动画:
)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{ if(operation == UINavigationControllerOperationPush){ //Push return 返回你的自定义动画 } if(operation == UINavigationControllerOperationPop){ //Pop return 返回你的自定义动画 } return nil; }
自定义转场动画实现代码:
//Pop
- (void)CreatePopAnimation:(id )transitionContext{
UIViewController * fromVC = [_context viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
[containerView addSubview:fromVC.view];
maskLayer.path = SmallPath.CGPath;
fromVC.view.layer.mask = maskLayer;
CABasicAnimation *anim = [CABasicAnimation animation];
anim.keyPath = @"path";
anim.fromValue = (__bridge id _Nullable)(BigPath.CGPath);
anim.toValue = (__bridge id)(SmallPath.CGPath);
anim.duration = [self transitionDuration:_context];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.delegate = self;
[maskLayer addAnimation:anim forKey:nil];
}
//push
- (void)CreatePushAnimation:(id )transitionContext{
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
maskLayer.path = BigPath.CGPath;
toVC.view.layer.mask = maskLayer;
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"path"];
anim.fromValue = (__bridge id)(SmallPath.CGPath);
anim.toValue = (__bridge id)((BigPath.CGPath));
anim.duration = [self transitionDuration:_context];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.delegate = self;
[maskLayer addAnimation:anim forKey:nil];
}
Presentation&Dismiss形式转场动画
-
自定义转场动画入口:
- present后的视图
ToVC
遵守UIViewControllerTransitioningDelegate
协议- self.transitioningDelegate = self;
- self.modalPresentationStyle = UIModalPresentationCustom;
- 实现下面方法,并返回自定义动画:
- (nullable id
)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{ } - (nullable id )animationControllerForDismissedController:(UIViewController *)dismissed{ } - present后的视图
自定义动画代码实现
//dissmiss
- (void)CreateDismissAnimation:(id )transitionContext{
UIView* containerView = [transitionContext containerView];
TwoVC * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[containerView addSubview:fromVC.view];
maskLayer.path = SmallPath.CGPath;
fromVC.view.layer.mask = maskLayer;
CABasicAnimation *anim = [CABasicAnimation animation];
anim.keyPath = @"path";
anim.fromValue = (__bridge id _Nullable)(BigPath.CGPath);
anim.toValue = (__bridge id _Nullable)(SmallPath.CGPath);
anim.duration = [self transitionDuration:_context];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.delegate = self;
[maskLayer addAnimation:anim forKey:nil];
}
//Presentation
- (void)CreatePresentationAnimation:(id )transitionContext{
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
toVC.view.frame = CGRectMake(0, 0, GGScreenwidth, GGScreenheight);
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
maskLayer.path = BigPath.CGPath;
toVC.view.layer.mask = maskLayer;
CABasicAnimation *anim = [CABasicAnimation animation];
anim.keyPath = @"path";
anim.fromValue = (__bridge id _Nullable)(SmallPath.CGPath);
anim.toValue = (__bridge id _Nullable)(BigPath.CGPath);
anim.duration = [self transitionDuration:_context];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.delegate = self;
[maskLayer addAnimation:anim forKey:nil];
}
转场动画代码 我已经集成了Push&Pop和Present&Dismiss,码字不容易,码代码更不容易,github顺手给个星星
转场效果: