帧动画例子: UImageView帧动画,多张图片一帧一帧播放的动画。UIView帧动画 用户可以交互。核心动画里面的帧动画,移动缩放旋转等效果,用户不能交互。
渐变动画:控件从一个位置移动到另一个位置,动画的形式。UIView动画、核心动画
核心动画:核心动画的所有操作都是在子线程进行,动画都是假象,真实位置并没有改变,用户也不能交互。如果要实现可以交互的用UIView动画,它是改变的真实位置
transform
# 平移
// CGAffineTransform 带make 和不带make的区别
//1. make 带相对于原始位置带形变操作(一般只用作一次形变)
[UIView animateWithDuration:2 animations:^{
self.imageView.transform = CGAffineTransformMakeTranslation(0, 100);
}];
//2.不带make相对一指定的形变开始进行
//相对于上一次的操作(一般用于多次形变)
[UIView animateWithDuration:2 animations:^{
self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, 0, 100);
}];
旋转
带make和不带make区别同上
// 参数穿的是弧度
self.label.transform = CGAffineTransformMakeRotation(M_PI);
self.label.transform = CGAffineTransformRotate(label.transform, M_PI_2);
# 缩放
带make和不带make区别同上
// 缩放比例1代表原始大小 大于1表示放大,小于1代表缩小
self.imageView.transform = CGAffineTransformMakeScale(1.5, 1.5);
self.imageView.transform = CGAffineTransformScale(self.imageView.transform, 1.5, 1.5);
上面这几个不能两个都使用,只使用一个,使用多个会出现和想象的不一样的效果,接下来会介绍其他的
CATransform3D
//坐标系如上,有大小有方向,矢量,向量
x:水平方向
y:竖直方向
z:面向自己的方向
// 坐标系,x,y,z通过向量计算
// 旋转
//参数 旋转弧度
layer.transform = CATransform3DMakeRotation(<#CGFloat angle#>, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>)
// 平移
layer.transform = CATransform3DMakeTranslation(<#CGFloat tx#>, <#CGFloat ty#>, <#CGFloat tz#>)
// 缩放
layer.transform = CATransform3DMakeScale(<#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)
隐式动画
每一个 UIView内部都默认关联着一个CALayer,我们称这个Layer,为 Root Layer(根层)
所有的非Root Layer,也就是手动创建的 Calayer对象,都存在着隐式动画
layer.position;
layer.bounds;
layer.contents;
// 隐式动画事务
CATransaction
// 开启一个事务
[CATransaction begin];
// 取消隐式动画
[CATransaction setDisableActions:YES];
// 设置动画时间
[CATransaction setAnimationDuration:2];
self.view.layer.position = CGPointMake(10, 10);
self.view.layer.bounds = CGRectMake(0, 0, 100, 100);
// 事务提交
[CATransaction commit];
Core Animation(框架) 继承图如下:
真正使用做动画的是这四个
CAAnimationGroup
CABasicAnimation
CAKeyframeAnimatin
CATransition
使用CGAffineTransform注意:
//通过 transform修改位置,不会修改 center
// transform修改的是 Frame
Core Animation的使用步骤:
如果不是 xcode5之后的版本,使用它需要先添加 Quartzcore. framework
和引入对应的框架<Quartz Core/QuartzCore.h>
开发步骤
- 首先得有
CALayer
- 初始化一个
Animation
对象,并设置一些动画相关属性 - 通过调用
CALayer
的addAnimation: forKey:
方法,増加CAAnimation
对象 到CALayer
中,这样就能开始执行动画了
CABasicAnimation动画
// 位移动画
- (void)basicAnimationForPositionY {
// 1. 创建动画对象
CABasicAnimation *animation = [CABasicAnimation animation];
// 2.设置动画属性
animation.keyPath = @"position.y";
animation.toValue = @(200);
// 动画完成时会自动删除动画
animation.removedOnCompletion = NO;
// 动画完成时保持什么状态
animation.fillMode = kCAFillModeForwards;
// 添加动画
[self.baseAnimationView.layer addAnimation:animation forKey:@"animation_position_y"];
}
// 心跳缩放动画
- (void)basicAnimationForTransformScale {
// 1. 创建动画对象
CABasicAnimation *animation = [CABasicAnimation animation];
// 2. 设置属性
animation.keyPath = @"transform.scale";
animation.toValue = @0;
// 设置动画执行次数
animation.repeatCount = MAXFLOAT;
// 设置动画执行时间
animation.duration = 0.5;
// 自动反转(怎么去怎么回来,去和回都以动画形式)
animation.autoreverses = YES;
//3. 添加动画
[self.baseAnimationView.layer addAnimation:animation forKey:nil];
}
帧动画
// 旋转动画
- (void)keyframeAnimationForTransformRotation {
#define angleToArc(angle) (angle * (M_PI / 180.f))
CAKeyframeAnimation *animaiton = [CAKeyframeAnimation animation];
animaiton.keyPath = @"transform.rotation";
// 设置动画旋转弧度
animaiton.values = @[@(angleToArc(-5)), @(angleToArc(5))];
// 设置动画执行一次需要的时间
animaiton.duration = 0.1;
// 设置动画重复次数
animaiton.repeatCount = MAXFLOAT;
// 设置动画反转
animaiton.autoreverses = YES;
[self.baseAnimationView.layer addAnimation:animaiton forKey:nil];
}
// 路径位移动画
- (void)keyfameAnimationForPosition {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(100, 288)];
[path addLineToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(300, 100)];
[path addQuadCurveToPoint:CGPointMake(300, 600) controlPoint:CGPointMake(400, 600)];
[path addLineToPoint:CGPointMake(100, 288)];
animation.path = path.CGPath;
animation.duration = 5.0;
animation.repeatCount = CGFLOAT_MAX;
// 根据路径自动旋转
animation.rotationMode = @"autoReverse";
// 设置时间计算模式
animation.calculationMode = @"paced";
[self.baseAnimationView.layer addAnimation:animation forKey:nil];
// 通过设置layer层contents指定图片。添加定时器形成动画
// self.baseAnimationView.layer.contents = (id)UIImage.new.CGImage;
}
转场动画
- (void)transitionAnimation {
// 专场动画
CATransition *animation = [CATransition animation];
// 设置动画专场类型
animation.type = @"moveIn";
// 设置动画方向
animation.subtype = @"fromTop";
// 设置动画从哪个位置开始动画
animation.startProgress = 0.2;
// 设置动画从哪个位置结束动画
animation.endProgress = 0.5;
animation.duration = 1.3;
[self.baseAnimationView.layer addAnimation:animation forKey:nil];
}
动画组
- (void)animationGroup {
#define angleToArc(angle) (angle * (M_PI / 180.f))
CABasicAnimation *animation1 = [CABasicAnimation animation];
animation1.keyPath = @"position.y";
animation1.toValue = @400;
CABasicAnimation *animation2 = [CABasicAnimation animation];
animation2.keyPath = @"transform.scale";
animation2.toValue = @0.5;
CAKeyframeAnimation *animation3 = [CAKeyframeAnimation animation];
animation3.keyPath = @"transform.rotation";
animation3.values = @[@(angleToArc(10)),@(angleToArc(-10))];
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = @[animation1,animation2,animation3];
groupAnimation.removedOnCompletion = NO;
groupAnimation.fillMode = kCAFillModeForwards;
groupAnimation.duration = 1.0;
groupAnimation.repeatCount = 3;
groupAnimation.autoreverses = YES;
[self.baseAnimationView.layer addAnimation:groupAnimation forKey:nil];
}
UIView动画与CoreAnimation动画区别?
- 核心动画作用在layer上
- 核心动画看到一切都是假象,并没有去修改属性的真实值
- UIView动画用户可以交互,本质也是作用在layer上的,
什么时候使用核心动画,什么时候使用UIView动画?
- 当需要与用户交互时,必须得要使用UIView动画
- 当做帧动画时, 转场动画时,使用核心动画
图片折叠
设置图片的上半部分只显示上半部分,下半部分只显示下半部分,改变锚点位置,然后围绕x轴旋转
#define maxImageWidth [UIScreen mainScreen].bounds.size.width
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.bottomImageView];
[self.view addSubview:self.topImageView];
// 添加平移手势
[self.view addSubview:self.foldingPanGestureView];
// 添加渐变层
[self.bottomImageView.layer addSublayer:self.gradientLayer];
}
#pragma mark - event response
- (void)panGes:(UIPanGestureRecognizer *)panGes {
CGPoint point = [panGes translationInView:panGes.view];
// 计算旋转弧度
// 找最大值 = M_PI
// 什么时候最大 point.y == view.height
//
CGFloat angle = M_PI / panGes.view.frame.size.height * point.y;
if (panGes.state == UIGestureRecognizerStateChanged) {
CATransform3D transform = CATransform3DIdentity;
// 400.0眼睛离屏幕的距离(近大远小)
transform.m34 = -1/400.0;
self.topImageView.layer.transform = CATransform3DRotate(transform, - angle, 1, 0, 0);
// 设置渐变层的不透明度
// 找最大值
// 什么时候最大
CGFloat opcity = point.y * 1.0 / self.foldingPanGestureView.bounds.size.height;
self.gradientLayer.opacity = opcity;
} else if (panGes.state == UIGestureRecognizerStateEnded) {
[UIView animateWithDuration:0.1 delay:0 usingSpringWithDamping:0.3 /*越小弹性越大*/initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear/*线性匀速*/ animations:^{
self.topImageView.layer.transform = CATransform3DIdentity;
self.gradientLayer.opacity = 0.0;
} completion:^(BOOL finished) {
}];
}
}
#pragma mark - getters and setters
- (UIImageView *)topImageView {
if (_topImageView == nil) {
UIImage *image = [UIImage imageNamed:@"badddpng"];
CGFloat w = maxImageWidth;
// 获取图片的高度,等比获取高度的一半
CGFloat h = image.size.height / image.size.width * w * 0.5;
_topImageView = [[UIImageView alloc] initWithImage:image];
_topImageView.contentMode = UIViewContentModeScaleAspectFill;
_topImageView.bounds = CGRectMake(0, 0, w, h);
// 设置imageView显示图片的坐标系范围
// 设置图片的上半部分只显示上半部分
_topImageView.layer.contentsRect = CGRectMake(0, 0, 1, 0.5);
// 设置imageView的锚点
_topImageView.layer.anchorPoint = CGPointMake(0.5, 1);
// 设置imageView的position,锚点跟随它移动
_topImageView.layer.position = self.view.center;
}
return _topImageView;
}
- (UIImageView *)bottomImageView {
if (_bottomImageView == nil) {
UIImage *image = [UIImage imageNamed:@"badddpng"];
CGFloat w = maxImageWidth;
// 获取图片的高度,等比获取高度的一半
CGFloat h = image.size.height / image.size.width * w * 0.5;
_bottomImageView = [[UIImageView alloc] initWithImage:image];
_bottomImageView.contentMode = UIViewContentModeScaleAspectFill;
_bottomImageView.bounds = CGRectMake(0, 0, w, h);
// 设置imageView显示图片的坐标系范围
// 设置图片的下半部分只显示下半部分
_bottomImageView.layer.contentsRect = CGRectMake(0,0.5, 1, 0.5);
// 设置imageView的锚点
_bottomImageView.layer.anchorPoint = CGPointMake(0.5, 0);
// 设置imageView的position,锚点跟随它移动
_bottomImageView.layer.position = self.view.center;
}
return _bottomImageView;
}
- (UIView *)foldingPanGestureView {
if (_foldingPanGestureView == nil) {
_foldingPanGestureView = [UIView new];
UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGes:)];
[_foldingPanGestureView addGestureRecognizer:panGes];
_foldingPanGestureView.bounds = CGRectMake(0, 0, maxImageWidth, CGRectGetHeight(self.topImageView.frame) + CGRectGetHeight(self.bottomImageView.frame));
_foldingPanGestureView.center = self.view.center;
}
return _foldingPanGestureView;
}
- (CAGradientLayer *)gradientLayer {
if (_gradientLayer == nil) {
// 渐变层
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
// 设置渐变的颜色
gradientLayer.colors = @[
(id)UIColor.clearColor.CGColor,
(id)UIColor.blackColor.CGColor,
];
gradientLayer.frame = self.bottomImageView.bounds;
// 设置渐变的方向
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(0, 1);
// 设置渐变的起始位置(从哪个点开始渐变到下一个颜色),其中下面的0.2,0.3是相对于整个gradientLayer的比例所在的位置
// gradientLayer.locations = @[@0.2,@0.5];
// 设置图层的不透明度
gradientLayer.opacity = 0;
_gradientLayer = gradientLayer;
}
return _gradientLayer;;
}
复制层layer
@interface XLShopReplicatorLayerView ()
@property (nonatomic, strong) CAReplicatorLayer *replicatorLayer;
@property (nonatomic, strong) CALayer *layer1;
@property (nonatomic, strong) CALayer *layer2;
@end
@implementation XLShopReplicatorLayerView
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor blackColor];
[self.layer addSublayer:self.replicatorLayer];
[self.replicatorLayer addSublayer:self.layer1];
[self.replicatorLayer addSublayer:self.layer2];
}
return self;
}
#pragma mark - getters and setters
- (CAReplicatorLayer *)replicatorLayer {
if (_replicatorLayer == nil) {
//1. 创建复制层layer(可以复制子层layer)
CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.frame = self.bounds;
replicatorLayer.backgroundColor = [UIColor blueColor].CGColor;
//2. 设置复制层的属性
//2.1 复制子层的份数(包括它自己),
replicatorLayer.instanceCount = 5;
//2.2 复制出来的子层做形变操作(相当于后复制出来的子层对上一个复制出来的子层做形变)
replicatorLayer.instanceTransform = CATransform3DMakeTranslation(45, 0, 0);
//2.3 后一个子图层和前一个子图层的时间间隔
replicatorLayer.instanceDelay = 0.3;
//3 添加复制层
_replicatorLayer = replicatorLayer;
}
return _replicatorLayer;
}
- (CALayer *)layer1 {
if (_layer1 == nil) {
// 子层layer1
CALayer *layer1 = [CALayer layer];
layer1.frame = CGRectMake(50, 50, 30, 30);
layer1.backgroundColor = [UIColor redColor].CGColor;
_layer1 = layer1;
}
return _layer1;
}
- (CALayer *)layer2 {
if (_layer2 == nil) {
// 子层layer2
CALayer *layer2 = [CALayer layer];
layer2.frame = CGRectMake(50, 150, 30, 30);
layer2.backgroundColor = [UIColor yellowColor].CGColor;
_layer2 = layer2;
}
return _layer2;
}
@end
音乐震动条
#define kMusicBarNum 8
#define kMusicBarMargin 10
@interface MusicShockBarView ()
@property (nonatomic, strong) UIView *musicShockBarContentView;
@property (nonatomic, strong) CAReplicatorLayer *replicatorLayer;
@property (nonatomic, strong) CALayer *musicBarLayer;
@property (nonatomic, strong) CABasicAnimation *musicBarBasicAnimation;
@end
@implementation MusicShockBarView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
[self addSubview:self.musicShockBarContentView];
[self.musicShockBarContentView.layer addSublayer:self.replicatorLayer];
[self.replicatorLayer addSublayer:self.musicBarLayer];
[self.musicBarLayer addAnimation:self.musicBarBasicAnimation forKey:nil];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.musicShockBarContentView.frame = self.bounds;
self.replicatorLayer.frame = self.bounds;
CGFloat musicBarWidth = [self musicBarWith];
CGFloat musicBarHeiht = [self musicBarHeight];
self.musicBarLayer.bounds = CGRectMake(0, 0, musicBarWidth,musicBarHeiht);
self.musicBarLayer.position = CGPointMake(kMusicBarMargin, CGRectGetMaxY(self.musicShockBarContentView.frame));
self.replicatorLayer.instanceTransform = CATransform3DMakeTranslation(musicBarWidth + kMusicBarMargin, 0, 0);
}
#pragma mark - private methods
- (UIColor *)randColor {
CGFloat r = arc4random_uniform(256);
CGFloat g = arc4random_uniform(256);
CGFloat b = arc4random_uniform(256);
return [UIColor colorWithRed:r / 255.f green:g /255.f blue:b/255.f alpha:1];
}
- (CGFloat)musicBarHeight {
return CGRectGetHeight(self.musicShockBarContentView.frame) / 2.f;
}
- (CGFloat)musicBarWith {
return (CGRectGetWidth(self.musicShockBarContentView.frame) - ((1+ kMusicBarNum) * kMusicBarMargin))/ (kMusicBarNum * 1.0);
}
#pragma mark - getters and setters
- (UIView *)musicShockBarContentView {
if (_musicShockBarContentView == nil) {
_musicShockBarContentView = [UIView new];
_musicShockBarContentView.backgroundColor = [UIColor whiteColor];
}
return _musicShockBarContentView;
}
- (CAReplicatorLayer *)replicatorLayer {
if (_replicatorLayer == nil) {
// 1. 创建复制层layer(可以复制子层layer)
CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.frame = self.bounds;
replicatorLayer.backgroundColor = [self randColor].CGColor;
//2. 设置复制层的属性
//2.1 复制子层的份数(包括它自己),
replicatorLayer.instanceCount = kMusicBarNum;
//2.2 复制出来的子层做形变操作(相当于后复制出来的子层对上一个复制出来的子层做形变)
replicatorLayer.instanceTransform = CATransform3DMakeTranslation(1, 0, 0);
//2.3 后一个子图层和前一个子图层的时间间隔
replicatorLayer.instanceDelay = 0.3;
//3 添加复制层
_replicatorLayer = replicatorLayer;
}
return _replicatorLayer;
}
- (CALayer *)musicBarLayer {
if (_musicBarLayer == nil) {
CALayer *layer = [CALayer layer];
layer.backgroundColor = [self randColor].CGColor;
layer.anchorPoint = CGPointMake(0, 1);
_musicBarLayer = layer;
}
return _musicBarLayer;
}
- (CABasicAnimation *)musicBarBasicAnimation {
if (_musicBarBasicAnimation == nil) {
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.scale.y";
animation.toValue = @(0.4);
animation.repeatCount = CGFLOAT_MAX;
animation.autoreverses = YES;
animation.duration = 0.7;
_musicBarBasicAnimation = animation;
}
return _musicBarBasicAnimation;
}
@end
图片倒影效果
@interface XLShopReplicatorLayerPictureReflectionView ()
@property (nonatomic, strong) UIImageView *reflectionImageView;
@end
@implementation XLShopReplicatorLayerPictureReflectionView
#pragma mark - life cycle
+ (Class)layerClass {
return [CAReplicatorLayer class];
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.reflectionImageView];
CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
layer.instanceCount = 2;
layer.instanceRedOffset -= 0.2;
layer.instanceGreenOffset -= 0.2;
layer.instanceBlueOffset -= 0.2;
layer.instanceAlphaOffset -= 0.1;
layer.instanceTransform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.reflectionImageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height / 2.f);
}
#pragma mark - getters and setters
- (UIImageView *)reflectionImageView {
if (_reflectionImageView == nil) {
_reflectionImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"banner1"]];
}
return _reflectionImageView;
}
@end
动画组结合复制层Layer,水波纹动画
@interface XLGroupView ()
@property (nonatomic, strong) CAReplicatorLayer *replicatorLayer;
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
@property (nonatomic, strong) CABasicAnimation *opacityAnimation;
@property (nonatomic, strong) CABasicAnimation *transformAnimation;
@property (nonatomic, strong) CAAnimationGroup *groupAnimation;
@end
@implementation XLGroupView
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor blueColor];
[self.layer addSublayer:self.replicatorLayer];
[self.replicatorLayer addSublayer:self.shapeLayer];
[self.shapeLayer addAnimation:self.groupAnimation forKey:nil];
}
return self;
}
- (instancetype)init {
if (self = [super init]) {
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.replicatorLayer.frame = self.bounds;
self.shapeLayer.frame = self.bounds;
self.shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
}
#pragma mark - getters and setters
- (CAReplicatorLayer *)replicatorLayer {
if (_replicatorLayer == nil) {
_replicatorLayer = [CAReplicatorLayer layer];
_replicatorLayer.frame = self.bounds;
_replicatorLayer.instanceCount = 4;
_replicatorLayer.instanceDelay = 0.5;
}
return _replicatorLayer;
}
- (CAShapeLayer *)shapeLayer {
if (_shapeLayer == nil) {
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.frame = self.bounds;
_shapeLayer.fillColor = [UIColor redColor].CGColor;
_shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
_shapeLayer.opacity = 0;
}
return _shapeLayer;
}
- (CABasicAnimation *)opacityAnimation {
if (_opacityAnimation == nil) {
_opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
_opacityAnimation.fromValue = @(0.3);
_opacityAnimation.toValue = @(0);
}
return _opacityAnimation;
}
- (CABasicAnimation *)transformAnimation {
if (_transformAnimation == nil) {
_transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
_transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0, 0, 0)];
_transformAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1, 1, 0)];
}
return _transformAnimation;
}
- (CAAnimationGroup *)groupAnimation {
if (_groupAnimation == nil) {
_groupAnimation = [CAAnimationGroup animation];
_groupAnimation.animations = @[self.opacityAnimation,self.transformAnimation];
_groupAnimation.duration = 3.0;
_groupAnimation.repeatCount = CGFLOAT_MAX;
}
return _groupAnimation;
}
@end