动画

UIView动画



动画_第1张图片
对各个属性进行动画效果



动画_第2张图片
第2个,是UIViewAnimation的blocks方法,方便开发。第3个是针对KeyframeAnimations的接口


1.UIView(UIViewAnimation)


动画_第3张图片
所有的动画效果都在这两句代码之间



动画_第4张图片


- (void)startAnimating

{

    [UIView beginAnimations:@"squareAnimation" context:(__bridge void *)(self)];//第一个参数是id,之后会用到,第二个参数context 是上下文的意思。

    [UIView setAnimationDelegate:self];

    [UIViewsetAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    [UIView setAnimationDuration:0.4]; 设置运动时间,不设置的话默认是0.2

    [UIView setAnimationRepeatCount:2];//重复执行次数

    [UIView setAnimationRepeatAutoreverses:YES];//动画自动往回走

    _square.center = CGPointMake(_square.center.x + 100, _square.center.y);往右偏移100,view移动需要放到最后面执行,要不会和预期的效果不一样

    [UIView commitAnimations];

}



动画_第5张图片



动画_第6张图片
1.EaseInOut开始和结尾比较慢,中间比较快 2.EaseIn  开始比较慢后面速度将变快 3.EaseOut 开始比较快最后比较慢 4.Linear  整个过程都是匀速的。



动画_第7张图片
参数1,动画效果,参数2,对哪一个view操作,参数3  YES 只会显示当前的动画效果,其他的操作所生成动画都会被覆盖,NO 同时显示其他的动画效果。
动画_第8张图片
这里FlipFromLeft 从左边翻转



动画_第9张图片
这里在翻转的是_rootView而不是square

- (void)viewDidLoad

{

    [super viewDidLoad];


    self.view.backgroundColor = [UIColor whiteColor];

    self.title = @"Flip animation";


    _rootView = [[UIView alloc] init];

    _rootView.bounds = CGRectMake(0, 0, 50, 50);

    _rootView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));

    _rootView.backgroundColor = [UIColor whiteColor];

    [self.view addSubview:_rootView];


    _square1 = [[UIView alloc] init];

    _square1.backgroundColor = [UIColor orangeColor];

    _square1.frame = _rootView.bounds;

    [_rootView addSubview:_square1];


    _square2 = [[UIView alloc] init];

    _square2.backgroundColor = [UIColor yellowColor];

    _square2.frame = _square1.frame;

    _square2.hidden = YES;

    [_rootView addSubview:_square2];

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event

{

    [self startFliping];

//    [self startBlockFliping1];

//    [self startBlockFliping2];

}

- (void)startFliping

{

    [UIView beginAnimations:@"squareAnimation" context:(__bridge void *)(self)];

    [UIView setAnimationDuration:1];

    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:_rootView cache:YES];

    _square1.hidden = !_square1.hidden;

    _square2.hidden = !_square2.hidden;

    [UIView commitAnimations];

}

typedefNS_ENUM(NSInteger, UIViewAnimationTransition) {

    UIViewAnimationTransitionNone,//无效果

    UIViewAnimationTransitionFlipFromLeft,//从左边翻转

    UIViewAnimationTransitionFlipFromRight,//从右边翻转

    UIViewAnimationTransitionCurlUp,//向上翻纸的效果

    UIViewAnimationTransitionCurlDown,//向下翻纸的效果

};


Block动画实现

动画_第10张图片
block动画实现



动画_第11张图片
改写成block方法


   UIViewAnimationOptions options = UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat|UIViewAnimationOptionTransitionFlipFromLeft;


    [UIView animateWithDuration:0.4 delay:0 options:options animations:^{

         [UIView setAnimationRepeatCount:2];

        _square.center = CGPointMake(_square.center.x+100, _square.center.y+100);//操作UI的代码要放到最后面才能生效。

    }completion:^(BOOLfinished) {

        _square.center =CGPointMake(_square.center.x-100, _square.center.y-100);

    }];


动画_第12张图片
弹簧动画,相比之前的方法多出了两个参数



动画_第13张图片



动画_第14张图片
第二个在结尾的时候,出现了弹簧的效果



动画_第15张图片
翻转改写block,在这里cache相当于下面的duration, YES,会将里面会动的内容变成静态的跟着view一起动,NO,里面的内容也会自己动。为了一样注释掉了option第二个content的枚举,如果duration为0,需要解除content枚举的注释。



动画_第16张图片
加上options的第一个views枚举值,会和上面的hidden操作一致,否则会执行底下红色虚线的操作toview添加到superview,fromview从superview移除。


 UIViewAnimationOptionShowHideTransitionViews  = 1 <<  8,// flip to/from hidden state instead of adding/removing   //修改hidden属性来代替adding/removing操作



动画_第17张图片
block里多了3种翻转效果。

 UIViewAnimationOptionTransitionCrossDissolve  = 5 << 20, //渐变效果

    UIViewAnimationOptionTransitionFlipFromTop    = 6 << 20,向上翻转

    UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,向下翻转


//下面是两种block实现的动画效果


- (void)startBlockFliping1

{

    UIViewAnimationOptions options = UIViewAnimationOptionTransitionFlipFromLeft

                                /* | UIViewAnimationOptionAllowAnimatedContent */;

    [UIView transitionWithView:_rootView duration:1 options:options animations:^{

        _square1.hidden = !_square1.hidden;

//        _square2.hidden = !_square2.hidden;

    }completion:nil];

}

- (void)startBlockFliping2

{

    UIView *fromView = _square1.hidden ? _square2 : _square1;

    UIView *toView = _square1.hidden ? _square1 : _square2;

    UIViewAnimationOptions options = UIViewAnimationOptionTransitionFlipFromTop

                                   |UIViewAnimationOptionShowHideTransitionViews

                                /* | UIViewAnimationOptionAllowAnimatedContent */;

    [UIView transitionFromView:fromView toView:toView duration:1 options:options completion:nil];

}  


Problem


动画_第18张图片
实现这个效果,方块沿着虚线挪动回到原来的位置。



动画_第19张图片
解决上面的问题。



动画_第20张图片
关键帧动画可以解决problem的问题。



动画_第21张图片
指定这4个位置即可



动画_第22张图片
用这两个接口可以实现上面的解决沿虚线移动的问题。



动画_第23张图片
有4个运动轨迹,所以有4部,所以relativeDuration=1.0/ n个运动轨迹



动画_第24张图片
通过这些参数在原来动画的基础上来实现不同的位移效果
动画_第25张图片
左边block的动画,相对于右边keyframe的动画要生硬,因为左边只是将4个动画连了起来,右边的是把这个运动作为一个整体的动画来实现的。



动画_第26张图片
分解动画,一个是杆子放下来,一个是盘在旋转,实现思路:杆子改变指针,盘子改变transform


//

//  PlayViewController.m

//  UIViewAnimationDemo

//

//  Created by Chengyin on 16/7/3.

//  Copyright © 2016年 NetEase. All rights reserved.

//

#import "PlayViewController.h"

staticconstCGFloatcoverSize = 150.0f;

staticconstCGFloatdiskSize = 238.0f;

staticconstCGFloatneedleWidth = 162.0f;

staticconstCGFloatneedleHeight = 306.0f;

staticconstCGFloatneedleRotationDegree = -M_PI_4/ 1.5;

staticconstNSTimeIntervaldiskAnimationDuration = 20;

staticconstNSTimeIntervalneedleAnimationDuration = 0.3;

@interface PlayViewController ()

{

@private

    UIImageView*_needleView;

    UIImageView*_diskImageView;

    UIImageView*_coverimageView;


    BOOL_playing;

    BOOL_animating;

}

@end

@implementationPlayViewController

- (void)viewDidLoad

{

    [super viewDidLoad];

    self.title = @"Play animation";


    self.view.backgroundColor = [UIColor whiteColor];


    _coverimageView = [[UIImageView alloc] init];

    _coverimageView.image = [UIImage imageNamed:@"Cover.jpg"];

    [self.view addSubview:_coverimageView];


    _diskImageView = [[UIImageView alloc] init];

    _diskImageView.image = [UIImage imageNamed:@"Disk.png"];

    [self.view addSubview:_diskImageView];


    _needleView = [[UIImageView alloc] init];

    _needleView.image = [UIImage imageNamed:@"Needle.png"];

    [self.view addSubview:_needleView];


    _needleView.transform = CGAffineTransformMakeRotation(needleRotationDegree);//原来的图片是杆子是放下的,所以要让它抬起来需要改变transform

}

- (void)viewDidLayoutSubviews

{

    [super viewDidLayoutSubviews];


    _needleView.bounds = CGRectMake(0, 0, needleWidth, needleHeight);

    _needleView.center = CGPointMake(CGRectGetMidX(self.view.bounds), 64);


    _diskImageView.bounds = CGRectMake(0, 0, diskSize, diskSize);

    _diskImageView.center = CGPointMake(CGRectGetMidX(self.view.bounds), 64 + 80 + diskSize / 2);


    _coverimageView.bounds = CGRectMake(0, 0, coverSize, coverSize);

    _coverimageView.center = _diskImageView.center;

}

#pragma mark - needle

- (void)toggleNeedle:(void(^)(BOOLfinished))completion

{

    _animating = YES;

    _playing = !_playing;

    [UIView animateWithDuration:needleAnimationDuration animations:^{

        _needleView.transform = _playing ? CGAffineTransformIdentity : CGAffineTransformMakeRotation(needleRotationDegree);

    }completion:^(BOOLfinished) {

        _animating=NO;

        if(completion)

        {

            completion(finished);

        }

    }];

}

#pragma mark - play & stop

- (void)play

{

    if (_playing || _animating)

    {

        return;

    }


    [selftoggleNeedle:^(BOOLfinished) {

        [self startRotation];

    }];

}

- (void)stop

{

    if (!_playing || _animating)

    {

        return;

    }


    [self toggleNeedle:nil];

    [self stopRotation];

}

- (void)startRotation

{

    [UIView animateKeyframesWithDuration:diskAnimationDuration delay:0 options:UIViewKeyframeAnimationOptionRepeat|UIViewAnimationOptionCurveLinear animations:^{

  //不能直接设置360*,系统会自动选择最短路径来弄,所以看到的效果会是没动。需要按照下面的来设置才行。

        [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.25 animations:^{

            _diskImageView.transform = CGAffineTransformMakeRotation(M_PI_2);//1/4

            _coverimageView.transform = _diskImageView.transform;

        }];

        [UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.25 animations:^{

            _diskImageView.transform = CGAffineTransformMakeRotation(M_PI);//1/2

            _coverimageView.transform = _diskImageView.transform;

        }];

        [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{

            _diskImageView.transform = CGAffineTransformMakeRotation(M_PI_2 * 3);//3/2

            _coverimageView.transform = _diskImageView.transform;

        }];

        [UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{

            _diskImageView.transform = CGAffineTransformMakeRotation(M_PI * 2);//1

            _coverimageView.transform = _diskImageView.transform;

        }];

    }completion:nil];

}

- (void)stopRotation

{

    [UIView animateWithDuration:0 animations:^{

        _diskImageView.transform = CGAffineTransformIdentity;

        _coverimageView.transform = _diskImageView.transform;

    }];

}

#pragma mark - touch

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event

{

    if (_playing)

    {

        [self stop];

    }

    else

    {

        [self play];

    }

}

@end


CoreAnimation(1)



动画_第27张图片
底层的,UIView的animation是coreAnimation的封装


概念一、隐式动画:通过直接给属性赋值来做的



动画_第28张图片
view.alpha=0.2不会触发动画,layer.opacity=0.2会触发一个动画(从不透明变成0.2)旧值变成新的值得动画。CALayer大部分属性包含隐式动画,有些属性不包含,所以用的时候需要查看一下。



动画_第29张图片
UIView的画面展示其实都是由CALayer实现的



动画_第30张图片
uiview在赋值的时候通过某种手段把动画禁用掉了。


概念二、Layer树


动画_第31张图片
第一行的值无法改变,第二行:layer属性的最终的值,第三行:在整个动画过程中,layer在某一个时间点的近似值。



动画_第32张图片


概念三、CATransaction:打包当前时间点的所有动画操作


动画_第33张图片
左边的这些,都会听过右边的Render Tree进行渲染



动画_第34张图片
每一次的Render Tree渲染都会对应transaction这个事物



动画_第35张图片
可以通过在CATransaction begin和CATransaction commit之间加相应的隐式动画,配置一些代码来更改隐式动画的实现方式。


- (void)testTransaction

{

    [CATransaction begin];

    [CATransaction setCompletionBlock:^{

        NSLog(@"Set position animation completed.");

    }];

    [CATransaction setDisableActions:YES];

    [CATransaction setAnimationDuration:1];

    [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

    _myLayer.position = CGPointMake(_myLayer.position.x + 100, _myLayer.position.y);

    [CATransaction commit];

}


动画_第36张图片


- (void)disableAnimation

{

    [CATransaction begin];

    [CATransaction setDisableActions:YES];

    _myLayer.position = CGPointMake(_myLayer.position.x + 100, _myLayer.position.y);

    [CATransaction commit];

}



动画_第37张图片



动画_第38张图片
默认会有一个事务控制这个隐式动画



动画_第39张图片
不进行设置的话,动画默认执行时间为0.25秒



动画_第40张图片
虚线的是抽象类,不可以实例化,剩下的子类可以



动画_第41张图片



动画_第42张图片
animationWithKeyPath:@"",里面填写属性名(一般都和正常的属性名字相同),相对的有的属性是结构体,所以也可以这样写@"position.x"(position是结构体)



动画_第43张图片



动画_第44张图片
右边一列是左边一列的解释



动画_第45张图片



动画_第46张图片
这里  addAnimation这里是copy属性的,所以原先_myLayer内部的animation并不会被改变,不会因为@(150)而更改原来的动画位置



动画_第47张图片



动画_第48张图片
duration:执行动画的总时间,repeatCount:重复的次数,repeatDuration :重复时间(数学计算duration为2秒,repeatDuration为4秒,repeatDuration/duration=2  循环两次)不可以和repeatCount同时使用,





这样设置可以重复无限次



动画_第49张图片
隐式动画放在addanimation(显示动画之前),forkey的值要和隐方式的名字一致,position,就会只执行一个动画了。



动画_第50张图片



动画_第51张图片



动画_第52张图片
removeAllAnimations 移除当前视图layer上的所有动画,removeAnimationForkey移除单个动画



动画_第53张图片



动画_第54张图片
additive必须配合两个参数进行使用(fromvalue,byValue),否则会严重偏离轨迹,(添加两倍的距离)



动画_第55张图片
可以解决UIView动画取最短路径这件事



动画_第56张图片
360度的旋转动画。



动画_第57张图片



动画_第58张图片
所有的动画都有一个时间的系统,被称为CAMediaTiming System



动画_第59张图片
layer的创建的时间都是从系统开始的时间进行计算的。



动画_第60张图片
获取当前layer下的时间+1 ,指的是在1秒钟之后再开始动画,不能直接beginTime=2,这样的话,创建layer的时间已经过去了很多了。



动画_第61张图片
timeOffset:先做本身时间到结束时间间的动画,后做从0分开始到本身的动画,timeOffset不受speed控制的。timeoffset相对于整个一个时间点计算的。



动画_第62张图片



动画_第63张图片
不能使用透明部分的animation的代码,因为这个animation被创建的时候是copy出来的一份,不会更改原来的动画。



动画_第64张图片
layer通过convertTime获取全局时间



动画_第65张图片
根据计算公式来进行暂停动画效果

#import "CALayer+AnimationControl.h"

#pragma mark - pause & resume

- (void)pause

{

    if (!_playing || _animating)

    {

        return;

    }


    [self toggleNeedle:nil];

    [self pauseRotation];

}

- (void)resume

{

    if (_playing || _animating)

    {

        return;

    }


    [selftoggleNeedle:^(BOOLfinished) {

        [self resumeRotation];

    }];

}

- (void)pauseRotation

{

    [_diskImageView.layer ac_pause];

    [_coverimageView.layer ac_pause];

}

- (void)resumeRotation

{

    [_diskImageView.layer ac_resume];

    [_coverimageView.layer ac_resume];

}


#import "CALayer+AnimationControl.h"

@implementationCALayer (AnimationControl)

- (void)ac_pause

{

    CFTimeInterval localTime = [self convertTime:CACurrentMediaTime() fromLayer:nil];

    self.speed= 0.0;

    self.timeOffset= localTime;

}

- (void)ac_resume

{

    CFTimeInterval lastLocalTime = self.timeOffset;

    self.speed= 1.0;

    self.timeOffset= 0.0;

    self.beginTime= 0.0;

    CFTimeInterval localTime = [self convertTime:CACurrentMediaTime() fromLayer:nil];

    self.beginTime= localTime - lastLocalTime;

}

@end



动画_第66张图片


类目的地址:https://pan.baidu.com/s/17fdSz4uO7WewOg3EDCjxUg



fillmode=kCAFillModeBackwards 动画开始就会变成黄色(fromValue的值),fillmode=kcafillmodeforwards 动画结束之后继续保持tovalue状态,需要将removedOncompletion=NO,动画结束后不移除animation。KCAFillModeBoth两者都有


CoreAnimation 2


一、利用CALayer的锚点来对这个转轴进行抬起,放下操作


前文:用transform是对图片的中心点进行操作旋转

现在用CALayer来实现

staticconstCGFloatneedleWidth = 111.0f;

staticconstCGFloatneedleHeight = 183.0f;

staticconstCGFloatneedleAxisX = 30.0f;//距左30

staticconstCGFloatneedleAxisY = 30.0f;距离上边30

 CATransform3D transform = CATransform3DIdentity;

 _needleView.layer.anchorPoint = CGPointMake(needleAxisX / needleWidth, needleAxisY / needleHeight);//锚点计算,距离左边,上边30pt,anchorPoint是0到1,这里需要换算所以需要30除于图片宽度和高度

 transform.m34 = -1.0/2000;//设置视角才能让唱针显示,因为transform是三维的,这个值需要尽量的小。


 _needleView.frame = CGRectMake(self.view.bounds.size.width / 2 - needleAxisX, navigationHeight - needleAxisY, needleWidth, needleHeight);//唱针的frame计算

你可能感兴趣的:(动画)