Core Animation

帧动画例子: 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

截屏2021-03-23 下午3.24.27.png
//坐标系如上,有大小有方向,矢量,向量
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

Core animation.png

使用CGAffineTransform注意:

//通过 transform修改位置,不会修改 center 
// transform修改的是 Frame

Core Animation的使用步骤:
如果不是 xcode5之后的版本,使用它需要先添加 Quartzcore. framework 和引入对应的框架<Quartz Core/QuartzCore.h> 开发步骤

  1. 首先得有 CALayer
  2. 初始化一个 Animation对象,并设置一些动画相关属性
  3. 通过调用CALayeraddAnimation: forKey:方法,増加 CAAnimation对象 到 CALayer中,这样就能开始执行动画了

支持keypath的动画属性

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;
}

转场动画

CATransition.type属性值.png
- (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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容