1.UIView动画原理是什么
iOS 4.0之后提供了几个基于block块的动画方法,用于快速实现一些简单常用的UI动画效果。这种方式创建的动画为隐式动画,开发者只需要指定UI元素的一些属性的目标值,即可得到属性从当前值平滑过渡到目标值的动画效果,同时可以指定动画的持续时间。
可以设置UI隐式动画的常用属性主要有:frame、bounds、center、transform、alpha、backgroundColor、contentStretch等。
例如实现一个让视图平滑移动到指定位置的动画代码如下:
[UIView animateWithDuration:0.3 animations:^{
// 设置目标值
} completion:^(BOOL finished) {
// 动画执行结束回调
}];
可在动画结束后继续开始新的动画,实现简单的连续动画效果。
2.什么是隐式动画和显式动画
隐式动画是UIKit动画的基础,是iOS创建动态UI界面最直接的一种方式。开发者通过直接设定UI元素的一些可见属性的目标值,如frame、bounds、center、transform、alpha、backgroundColor、contentStretch等,即可自动生成属性变化的过渡动画。
隐式动画是一种默认动画,动画是线性的,可以满足基本的需求,但对于一些复杂的动画,如让视图沿曲线移动,隐式动画就无能为力了,需要定义显示动画来实现。
如视图view位于屏幕外,通过执行下面的隐式动画,view可以平滑地移入当前的视图中央
[UIView animateWithDuration:0.5 animations:^{
// 设置目标值
view.center = self.view.center;
} completion:^(BOOL finished) {
// 动画执行结束回调
}];
显示动画不像隐式动画那样默认从一个初始状态线性变化到目标状态,而是需要显式地定义完整的动画流程,这样略微复杂的同时会更加灵活,可以实现更加复杂的动画效果。简单来说,显示动画就是要显式地定义动画对象,设置动画的各个状态和值,然后将动画对象应用到视图上,即可实现动画的效果。
例如,下面定义一个在x和y轴方向上不断缩放的动画对象,动画使视图先放大1.2倍,动画结束后回到初始状态,如此循环。应用的时候,将该动画对象添加到对应视图的layer层上即可。
// 定义基本动画对象
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transformPath"];
// 设置动画目标状态,xy平面放大1.2倍
CATransform3D scaleTransform = CATransform3DMakeScale(1.2, 1.2, 1);
animation.toValue = [NSValue valueWithCATransform3D:scaleTransform];
// 动画持续时间
animation.duration = 0.5;
// 不断循环重复
animation.repeatCount = HUGE_VAL;
// 自动逆动画
animation.autoreverses = YES;
// 动画结束移除之前动画对视图的影响,回到初始状态
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
// 应用动画
[view.layer addAnimation:animation forKey:@"animationScaleKey"];
3.隐式动画原理,如何禁用图层的隐式动画
在iOS开发中,有时仅仅改变了图层或者视图的一个动画属性(如背景色),就会发现属性是平滑地过渡到目标值的,属性值的改变产生了一个动画效果,而实际上开发者并没有显式地使用动画,这就是隐式动画的作用。
隐式动画指当更改视图的非根图层的可动画属性时,CoreAnimation自动决定如何并且何时去做动画。动画的执行时间取决于当前事务(CATransition),而动画类型取决于图层行为。
CATransition类是用来包含一系列属性动画集合的机制,任何用指定事务去改变可动画属性都不会立即发生变化,而是当事务提交的时候开始用一个动画过渡到新值。CATransition类没有属性或者实例方法,也不能用alloc和init方法创建,但是可以用begin和commit方法分别来入栈和出栈。其次,还可以用+setAnimationDuration设置当前事务的动画时间,或者+AnimationDuration方法获取值。
改变属性是CALayer自动应用的动画被称为"行为"。当CALayer属性被修改时,他会调用actionForKey:方法,传递属性的名称。
具体的实现原理可以分为以下几步:
1.图层会检测其是否有代理,并且代理是否实现了- actionForLayer:forKey:方法,如果有,那么直接调用并返回结果。
2.如果图层没有代理,或者代理没有实现- actionForLayer:forKey:方法,那么图层会接着检查包含属性名称对应行为映射的actions字典。
3.如果actions字典没有包含对应的属性,那么图层会接着在他的style字典搜索属性名。
4.最后,如果在style字典中也没有找到对应的行为,那么图层将会直接调用定义每个属性的标准行为的-defaultActionForKey:方法。
于是就可以解释UIKit是如何禁用隐式动画的,每个UIView都是其根图层的代理,并且实现了- actionForLayer:forKey:方法,只不过返回nil,也就是不执行任何动画。
除了在- actionForLayer:forKey:方法的实现中返回nil外,还可以通过CATransition的方法来打开或者关闭属性的隐式动画。使用临时取消隐式动画的代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
_colorLayer = [CALayer layer];
_colorLayer.bounds = CGRectMake(0, 0, 200, 200);
_colorLayer.position = self.view.center;
_colorLayer.backgroundColor = [UIColor redColor].CGColor;
_colorLayer.delegate = self;
[self.view.layer addSublayer:_colorLayer];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[CATransaction begin];
// 关闭隐式动画
CATransaction.disableActions = YES;
CATransaction.animationDuration = 5;
_colorLayer.backgroundColor = [UIColor yellowColor].CGColor;
[CATransaction commit];
}
4.CGAffineTransform和CATransform3D分别有什么作用
CGAffineTransform被称为“仿射变换”,它被定义在Core Graphics框架中,主要用于在二维平面内对视图进行旋转、缩放和平移。事实上,CGAffineTransform是一个可以和二维空间向量(如CGPoint)做乘法的3*2的矩阵。CGAffineTransform中的“仿射”的意思是无论使用什么值的变换矩阵,图层中平行的两条线在变换之后仍然会保持平行。
Core Graphics框架提供了一系列函数来创建CGAffineTransform实例。主要有以下几种:
// 平移
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty);
// 缩放
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
// 旋转
CGAffineTransformMakeRotation(CGFloat angle);
UIView的 transform 属性和 CALayer 的 affineTransform 属性都是CGAffineTransform 类型,可以使用他们对视图或者图层进行仿射变换。此外,Core Graphics框架还提供了一些可以进行混合变换的函数,能够在一个变化的基础上做更深层次的变换,甚至可以将两个已经存在的变换矩阵进行合并。如果要做多次变换的操作,那么这些函数就会非常有用,下面用一个例子包含一个缩放、旋转和移动的变换,代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
CGSize size = self.view.frame.size;
_searchImageView = [[UIImageView alloc] initWithFrame:CGRectMake((size.width - 200) * 0.5, (size.height - 200) * 0.5, 200, 200)];
_searchImageView.backgroundColor = [UIColor
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
// 创建一个空的CGAffineTransform
CGAffineTransform transform = CGAffineTransformIdentity;
// 缩放
transform = CGAffineTransformScale(transform, 0.5, 0.5);
// 旋转
transform = CGAffineTransformRotate(transform, M_PI_4);
// 平移
transform = CGAffineTransformTranslate(transform, 100, 0);
_searchImageView.transform = transform;
}
CATransform3D是CoreAnimation 结构体,和 CGAffineTransform 一样,也是一个矩阵。CATransform3D主要用来做更复杂的关于CALayer的3D操作。CoreAnimation提供了一系列方法来创建和组合 CATransform3D 的矩阵。这些函数和 CGAffineTransform 类似,只是多了一个 z 参数,并且旋转函数除了 angle 之外还多了x、y、z 这3个参数,这些参数分别决定了每个坐标轴方向上的旋转。主要函数如下:
// 3D平移
CATransform3DMakeTranslation(CGFloat tx, CGFloat ty, CGFloat tz);
// 3D 旋转
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z);
// 3D缩放
CATransform3DMakeTranslation(CGFloat tx, CGFloat ty, CGFloat tz);
此外可以通过CATransform3D中的 m34 元素来控制 3D 变换的透视效果。m34 元素主要用于按比例缩放X和Y的值来计算远离视角的距离。m34 的默认值为0,可以通过设置其为 -1/d 来应用透明效果,d 代表了视角和屏幕之间的距离,d越小,透光效果越强,一般设置为500 ~ 1000.代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
CGSize size = self.view.frame.size;
_searchImageView = [[UIImageView alloc] initWithFrame:CGRectMake((size.width - 200) * 0.5, (size.height - 200) * 0.5, 200, 200)];
_searchImageView.image = [UIImage imageNamed:@"111.jpg"];
[self.view addSubview:_searchImageView];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = - 1.0 / 500;
transform = CATransform3DRotate(transform, M_PI_4, 0, 1,0);
_searchImageView.layer.transform = transform;
}
效果如下:

5.CATransition中过渡类型动画有哪几种style
CATransition 是 CoreAnimation 框架提供的转场动画类,开发者可以通过一组预定义的转换或者定制的CIFilter 实例来指定转场效果。下面代码是当点击tabbar 按钮时的界面切换动画。
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(nonnull UITabBarItem *)item {
CATransition *animation = [CATransition animation];
animation.duration = 0.5;
// 指定效果
animation.type = @"reveal";
animation.subtype = @"fromBottom";
[self.view.layer addAnimation:animation forKey:nil];
}
其中的 type 是一个 NSString 类型的字符串,指定了转场动画的类型,有4个效果可选择:
1.fade:淡入淡出的过渡效果。对应常量 kCATransitionFade。
2.moveIn:新视图移动到旧视图的上面,对应常量 kCATransitionMoveIn。
3.push:新视图将旧视图推出窗口。对应常量 kCATransitionPush。
4.reveal:旧视图移开显示下面的新视图,对应常量 kCATransitionReveal。
需要注意的是,以上4种效果都暴露在 CATransition 的.h中,可以安全使用。除此之外,还有一些效果属于iOS的私有 API ,使用这些效果后,应用在审核时有一定被拒绝的风险。这些效果包括:
1.cube:立方体效果。
2.oglFlip:翻转效果。
3.suckEffect:收缩效果。
4.rippleEffect:水滴波纹效果。
5.pageCurl:向上翻页效果。
6.pageUnCurl:向下翻页效果。
7.cameraIrisHollowOpen:摄像头打开效果。
8.cameraIrisHollowClose:摄像头关闭效果。
6.如何使用UIView动画自定义过渡动画
CATransition虽然是一种对那些不太好做平滑属性的强大工具,但是他的缺点也很明显,那就是提供的动画类型太少了。除此之外,可以使用UIView提供的过渡动画API。
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion API_AVAILABLE(ios(4.0));
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion API_AVAILABLE(ios(4.0));
和CATransition一样,UIView提供的过渡动画类型仍然很少,其中options参数可以指定如下常量。
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown,
};
在实际开发中,有时CATransition和UIView提供的过渡效果并不能满足项目需求,这时就必须考虑实现自定义的过渡动画了。
过渡动画的基础原则就是对原始图层外观截图,然后添加一段动画,平滑过渡到图层改变之后那个截图的效果。CALayer的renderInContext:方法可以将当前图层内容绘制成图片,然后在视图中显示出来。如果将这个截屏视图置于原始视图之上,那么就可以遮住真实视图的所有变化,于是就可以通过简单的UIView的动画API创建过渡效果。对当前视图状态截图,然后在改变背景图片的时候对截图快速转动并且淡出,示例代码如下:
- (void)startCustomTransitionAnimation {
// 获取截图
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, NO);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
// 将截图添加到主管图
UIImageView *coverImageView = [[UIImageView alloc] initWithImage:coverImage];
coverImageView.frame = self.view.bounds;
[self.view addSubview:coverImageView];
// 更换背景图
self.view.layer.contents = (id)[UIImage imageNamed:@"niu.jpg"].CGImage;
// 添加动画
[UIView animateWithDuration:1 animations:^{
CGAffineTransform transform = CGAffineTransformMakeScale(0.01, 0.01);
transform = CGAffineTransformRotate(transform, M_PI_2);
coverImageView.alpha = 0;
coverImageView.transform = transform;
} completion:^(BOOL finished) {
[coverImageView removeFromSuperview];
}];
}
7.如何理解并使用CAKeyframeAnimation?
CAKeyframeAnimation即关键帧动画,它是CAPropertyAnimation的一个子类,它作用于单一的属性,但和CABaseAnimation不一样的是,它不需要设置一个起始和结束的值,而是可以根据开发者提供的一连串随意的值来做动画。这些值就是关键帧,每帧之间的绘制将由系统自动完成。示例代码如下:
- (void)startKeyframeAnimation {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
// 指定需要做动画的属性
animation.keyPath = @"backgroundColor";
// 指定关键帧
animation.values = @[(id)[UIColor whiteColor].CGColor, (id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor, (id)[UIColor blackColor].CGColor, (id)[UIColor whiteColor].CGColor];
// 指定关键帧的过渡时间
animation.keyTimes = @[@0, @0.2, @0.5, @0.7, @1];
// 指定关键帧的过渡缓冲方式
animation.duration = 5;
animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[self.view.layer addAnimation:animation forKey:nil];
}
使用关键帧动画需要注意以下3点:
1.注意到 values 数组中开始和结束的颜色都是白色,这是因为关键帧动画并不能将当前值作为第一帧。动画在执行后会立即跳转到第一帧的值,然后在动画结束的时候突然恢复到原始值。所以为了使动画更加平滑自然,需要使用开始和结束的关键帧来匹配当前属性的值。
2.除了使用values来指定关键帧以外,还可以使用其path属性,它是CGPath类型,它可以用一种非常直观的方式来定义动画的运动序列。在做一些基于位置改变的动画时,它非常有用。
8.如何自定义UIViewController之间的转场动画?
开始自定义转场动画之前,需要了解几个简单的概念。
- fromView 和 toView,在很多API中常常会有fromView 和 toView,fromView表示当前视图,toView代表跳转后的视图(目标视图)。
- presentedViewController 和 presentingViewController,presentedViewController表示被 modal 出的视图控制器(或称为跳转的目标视图控制器),而presentingViewController表示源视图控制器。
- UIViewControllerTransitioningDelegate 协议用于为跳转动画提供实现了UIViewControllerTransitioning 协议的对象。
- UIViewControllerAnimationedTransitioning 协议主要用于控制动画的展示时间和动画展示逻辑。
- UIViewControllerInteractiveTransitioning 协议即交互式转场动画代理,这个协议主要用于交互式动画。
- UIViewControllerContextTransitioning 协议即转场动画上下文协议,它的作用在于为动画提供必备的信息。开发者不应该缓存任何关于动画的信息,而是应该从转场动画上下文中获取,这样可以保证总是获取到最新的、正确的信息。
要实现整个转场动画逻辑需要2组必需的元素,一是代表页面跳转关系的2个控制器对象,二是动画之行逻辑。以下是实现动画的步骤:
1.为源控制器和目标控制器分别设置一个遵守UIViewControllerAnimationedTransitioning协议的代理对象,当然也可以设置为同一个对象。
2.调用对应的跳转方法。此时系统会自动请求动画代理提供动画逻辑对象。
9.如何保持视图界面为动画结束时的状态?
在实际开发中,有时会发现动画结束时,视图状态会快速返回到初始状态。这主要是因为在动画过程中看到的只是呈现图层的效果,而图层属性真正的值并非是呈现的效果值。为了使视图状态停留在动画结束的那一刻,需要进行如下配置:
1.设置动画对象的 removeOnCompletion 属性为NO,这使得动画结束的时候仍然保持之前的状态。
2.设置动画对象的 fillMode 属性为 kCAFillModeForwards 。这个属性就是所谓的“填充”。当动画结束时,填充动画状态,使得动画在技术后仍保持结束那一刻的值。
3.为动画属性添加一个非空的健,以便在不需要动画的时候将它从图层上移除。
网友评论