动画

UIView动画



对各个属性进行动画效果



第2个,是UIViewAnimation的blocks方法,方便开发。第3个是针对KeyframeAnimations的接口


1.UIView(UIViewAnimation)


所有的动画效果都在这两句代码之间




- (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];

}





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



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



这里在翻转的是_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动画实现

block动画实现



改写成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);

    }];


弹簧动画,相比之前的方法多出了两个参数





第二个在结尾的时候,出现了弹簧的效果



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



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


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



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


实现这个效果,方块沿着虚线挪动回到原来的位置。



解决上面的问题。



关键帧动画可以解决problem的问题。



指定这4个位置即可



用这两个接口可以实现上面的解决沿虚线移动的问题。



有4个运动轨迹,所以有4部,所以relativeDuration=1.0/ n个运动轨迹



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



分解动画,一个是杆子放下来,一个是盘在旋转,实现思路:杆子改变指针,盘子改变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)



底层的,UIView的animation是coreAnimation的封装


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



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



UIView的画面展示其实都是由CALayer实现的



uiview在赋值的时候通过某种手段把动画禁用掉了。


概念二、Layer树


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




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


左边的这些,都会听过右边的Render Tree进行渲染



每一次的Render Tree渲染都会对应transaction这个事物



可以通过在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];

}



- (void)disableAnimation

{

    [CATransaction begin];

    [CATransaction setDisableActions:YES];

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

    [CATransaction commit];

}





默认会有一个事务控制这个隐式动画



不进行设置的话,动画默认执行时间为0.25秒



虚线的是抽象类,不可以实例化,剩下的子类可以





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





右边一列是左边一列的解释





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





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





这样设置可以重复无限次



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







removeAllAnimations 移除当前视图layer上的所有动画,removeAnimationForkey移除单个动画





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



可以解决UIView动画取最短路径这件事



360度的旋转动画。





所有的动画都有一个时间的系统,被称为CAMediaTiming System



layer的创建的时间都是从系统开始的时间进行计算的。



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



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





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



layer通过convertTime获取全局时间



根据计算公式来进行暂停动画效果

#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




类目的地址: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计算

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

推荐阅读更多精彩内容