UIView层
Block动画
animationWithDuration
一般是通过block的方式来实现,主要有以下几个函数:
1 | + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations |
这个函数是最简单的动画函数,duration定义了动画的时长,animation block定义了动画的内容,一般是设置动画的结束状态的语句,中间的具体动画过程则是又系统自动填充。
1 | + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion |
这个函数是在上一个动画函数的基础上添加了动画结束时候的回调block函数
1 | + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion |
这个动画函数是在上一个动画函数的基础上添加了延迟时长delay和一些动画选项UIViewAnimationOptions。UIViewAnimationOptions主要包含四大类:
-
一是基础属性相关,以UIViewAnimationOptions为前缀,如是否重复执行,是否反向执行等等;
-
二是动画的运行平缓度相关,以UIViewAnimationOptionsCurve为前缀,主要是EaseInOut、EaseIn、EaseOut和Layer,用于定义动画如何开始和结束;
-
三是方向相关,用于定义部分带方向动画,以UIViewAnimationOptionTransition为前缀;
-
四是帧数相关,用于定义动画的帧数,以UIViewAnimationOptionPreferredFramesPerSecond为前缀。
1 | + (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 |
这个动画函数是上一个动画函数的另一种情况,弹簧动画,主要的几个参数如下:
-
(CGFloat)dampingRatio,抑制系数,范围0-1之间,1表示在重点附近完全无摆动,越靠近1则摆动越大;
-
(CGFloat)velocity,相对的初始速度,范围0-1之间,表示属性值在一开始到达终止值的速度,设置为x则表示需要1/x秒可以到达终止值(这并不意味着动画的结束,因为是弹簧动画,物体的属性值还进行往返运动)。
1 | + (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion |
关键帧动画,通过定义动画过程中的关键帧,来实现对动画的控制,其主要的两个参数如下:
-
(UIViewKeyframeAnimationOptions)options,主要分为两类:
-
以UIViewKeyframeAnimationOption为前缀,类似UIViewAnimationOptions中的第一类;
-
以UIViewKeyframeAnimationOptionCalculationMode为前缀,主要表示的是不同的插值方式;
-
-
(void (^)(void))animations,用于添加关键帧,有如下函数
1 | + (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations |
其中frameStartTime和frameDuration均为0-1之间的值,即为相对于整个duration的值。
transitionFromView
1 | + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion; |
此处显式指定了fromView和toView
1 | + (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion; |
此处指定的view为containerView,即所做动画均为对这个containerView所做,动画效果都是以整个containerView作为基础进行的。
Layer层
CATransaction事务类
CATransaction类是Core Animation类中的事务类,在iOS中的图层中,图层的每个改变都是事务的一部分,CATransaction可以对多个layer的属性同时进行修改,同时负责成批的把多个图层树的修改作为一个原子更新到渲染树。
CATransaction类分为隐式事务和显式事务,对隐式动画的修改只能通过CATransaction,而显式动画可以通过CATransaction也可以通过其他方式修改。
隐式事务
所有对layer的属性的修改都会触发隐式动画,固定时长为0.25s,如果需要关闭隐式动画则需要调用如下函数:
1 | [CATransaction setDisableActions:YES]; |
显式事务
一般有如下结构:
1 | // 开始动画 |
此处在定义显式动画的同时会覆盖掉隐式动画,因此不需要手动关闭隐式动画。
⚠️UIView层的动画只能对指定view的rootLayer做动画,即
1 | CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init]; |
该代码是无效的,其实际产生的效果是这个shapeLayer的隐式动画,也就是一个时长为0.25s的动画,而并非是5s。要实现5s长的动画,就需要采用Layer层的动画,即:
1 | [CATransaction setDisableActions:YES]; // 取消layer的隐式动画 |
或者使用CATransaction来实现,即:
1 | [CATransaction begin]; |
这种方式则不需要关闭隐式动画。
CAAnimation类
主要实现类是CAAnimation及其子类,以及一个所有动画类都遵循的协议CAMediaTiming。
Layer层动画的添加方式:
1 | - (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key; |
其中key表示了要变化的属性的名字,例如:
1 | [self.curView.layer addAnimation:animation forKey:nil]; |
CAMediaTiming
所有动画类都需要实现的协议;
CAAnimation
动画基础类,不可直接使用;
CAPropertyAnimation
动画基础类,不可直接使用;
CABasicAnimation
基础属性动画,对keyPath所制定的属性进行变化,主要有以下几个函数和属性:
1 | @property(nullable, strong) id fromValue; // 初始值 |
有两个属性比较关键,可以用于设置动画结束之后是否需要回到初始态:
1 | // 不回到初始状态 |
这里就要涉及到CALayer的两个子layer对象,persentationLayer和modelLayer,前者负责显示出来的位置,后者负责实际的位置。即使我们在这里设置了动画结束之后不会到初始态,我们也只是将presentationLayer的值设置到了终止态,modelLayer的值还是原来的值,也就是此时通过类似layer.backgroundColor这样的get方法获得到的值是不会变的,要改变这个值则还需要显式的调用set方法(或者点调用)。
例如以下代码,实现了边框的宽度从90变到0的效果:
1 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"borderWidth"]; |
这段动画结束后,虽然显示出来的边框宽度是90,但是实际上self.view.layer.borderWidth获取到的值还是0.f。
CASpringAnimation
弹簧动画,其主要就是在CABasicAnimation的基础上增加了damping和initialVelocity这两个弹簧相关的对象;
CAKeyframeAnimation
关键帧动画,通过定义关键帧或者路径来实现动画的控制
关键帧
主要的参数如下
1 | @property(nullable, copy) NSArray *values; // 关键帧的值数组 |
#####路径
主要的参数如下
1 | @property(nullable) CGPathRef path; |
UIBezierPath主要是通过点来确定路径,同时我们可以定义点和点之间的路径类型,主要的几个函数如下:
1 | (void)moveToPoint:(CGPoint)point; // 设置初始节点的位置 |
例如下面的代码可以实现一个在固定路径上的关键帧动画:
1 | CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; |
CAAnimationGroup
组合动画,可以将多个动画组合起来,通过如下设置即可:
1 | // animationArr是一个由CAAnimation的子类构成的数组 |
注意此处该数组内的动画都是并发执行的,所以如果希望串行执行的话就需要通过设置这个动画组内的动画的beginTime和duration来实现。
CATransition
转场动画类
1 | CATransition *animation = [[CATransition alloc] init]; |
UIViewController的自定义转场动画
设两个UIViewController类分别为FirstViewController和SecondViewController,对应的实例分别为firstVC和secondVC,且显示顺序为firstVC->secondVC,要实现从firstVC到secondVC的显示动画和secondVC到firstVC的取消动画,需要以下几步:
- 实现一个遵循UIViewControllerAnimatedTransitioning的动画类AnimationController,并定义当前的TransitionType
1 | typedef NS_ENUM(NSUInteger, TransitionType) { |
- 在AnimationController类中实现如下两个方法:
1 | // 动画的duration |
-
duration的实现很简单,直接返回一个float即可;
-
动画的具体实现的主要流程如下:
-
首先根据type判断当前的操作具体是什么
-
对于push或者present,首先定义fromVC和toVC(注意这里在pop和dismiss的时候会产生fromVC和toVC的互换)
1 | UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; |
- 获取当前界面的截图视图snapshot,作为后续动画的实际操作view
1 | UIView *snapshot = [fromVC.view snapshotViewAfterScreenUpdates:NO]; |
- 获取containerView作为存放整个过程的容器
1 | UIView *containerView = [transitionContext containerView]; |
- 将fromVC设为hidden,并分别将toVC.view和snapshot加入到containerView中
1 | [fromVC.view setHidden:YES]; |
-
设定snapshot的初始设置和位置;
-
写动画
1 | NSTimeInterval duration = [self transitionDuration:transitionContext]; |
- 对于pop或者dismiss,首先定义fromVC和toVC(注意这里在pop和dismiss的时候会产生fromVC和toVC的互换)
1 | UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; |
- 获取containerView作为存放整个过程的容器
1 | UIView *containerView = [transitionContext containerView]; |
- 获取present和push中的截图视图snapshot,此时snapshot应当为containerView的subviews中的最后一个view
1 | UIView *snapshot = containerView.subviews.lastObject; |
- 在containerView中添加toVC.view
1 | [containerView addSubview:toVC.view]; |
-
设定snapshot的初始设置和位置;
-
写动画
1 | NSTimeInterval duration = [self transitionDuration:transitionContext]; |
- 对于present和dismiss,在FirstViewController中遵循UIViewControllerTransitioningDelegate,并实现animationControllerForPresentedController函数和animationControllerForDismissedController函数,返回转场动画的实例
1 | - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source |
- 对于push和pop,则需要在FirstViewController中遵循UINavigationControllerDelegate,并实现如下函数:
1 | - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC |