项目中用的动效总结

引文

这里举例UIView & CoreAnimation 中的相关用法

1. commitAnimations

基本写法,代码必须放在Begin和Commit之间:

[UIView beginAnimations:nil context:nil]; // 开始动画
// Code...
[UIView commitAnimations]; // 提交动画

示例代码

[UIView beginAnimations:nil context:nil]; // 开始动画
[UIView setAnimationDuration:10.0]; // 动画时长

/**
 *  图像向下移动
 */
CGPoint point = _imageView.center;
point.y += 150;
[_imageView setCenter:point];

[UIView commitAnimations]; // 提交动画

项目中用于页面翻转的动画:

-(void)flip{
    
    CGContextRef context=UIGraphicsGetCurrentContext();
    [UIView beginAnimations:nil context:context];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:1.0];
    UIView *topView ;
    
    
    //这里时查找视图里的子视图(这种情况查找,可能时因为父视图里面不只两个视图)
    NSInteger first= [[self.view subviews] indexOfObject:[self.view viewWithTag:100]];
    NSInteger second= [[self.view subviews] indexOfObject:[self.view viewWithTag:101]];
    topView = first < second ?[self.view viewWithTag:101]:[self.view viewWithTag:100];
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:topView cache:YES];

    [self.view exchangeSubviewAtIndex:first withSubviewAtIndex:second];
    
    //当父视图里面只有两个视图的时候,可以直接使用下面这段.
    
    //[self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
    
    
    [UIView setAnimationDelegate:self];
    [UIView commitAnimations];
    
    
}

2. 使用UIView的动画块代码:

这也是最常用的一种方式:

[UIView animateWithDuration:4.0 // 动画时长
            delay:2.0 // 动画延迟
          options:UIViewAnimationOptionCurveEaseIn // 动画过渡效果
         animations:^{
           // code...
         }
         completion:^(BOOL finished) {
           // 动画完成后执行
           // code...
         }];

对于使用Masonry的同学来说,使用这个方法需要注意调用mas_updateConstraints,使用方法相见GitHub。比如项目中用到的滚动label的效果:

    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        [ws.tipLbl mas_updateConstraints:^(MASConstraintMaker *make) {
            // make.centerY.equalTo(@(-50));
            make.center.equalTo(ws).centerOffset(CGPointMake(0, -1*ws.bounds.size.height));//25
        }];
        //[self.view updateConstraintsIfNeeded];
        [ws layoutIfNeeded];
    } completion:^(BOOL finished) {
        ws.tipLbl.text = tip;
        [self.tipLbl mas_updateConstraints:^(MASConstraintMaker *make) {
            // make.centerY.equalTo(@(10));
            make.center.equalTo(ws).centerOffset(CGPointMake(0, ws.bounds.size.height));
            
        }];
        
        [self layoutIfNeeded];
        [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
            [ws.tipLbl mas_updateConstraints:^(MASConstraintMaker *make) {
                // make.centerY.equalTo(@(-15));
                make.center.equalTo(ws);
            }];
            //[self.view updateConstraintsIfNeeded];
            [self layoutIfNeeded];
        } completion:^(BOOL finished) {
    
        }];
        
    }];

3. CoreAnimation

CABasicAnimation
CABasicAnimation : CAPropertyAnimation
指定动画的who(key path ),when(duration), where(from,to)使用示例:

- (void)animationbegin:(UIView *)view {
    /* 放大缩小 */
    
    // 设定为缩放
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    
    // 动画选项设定
    animation.duration = 0.1; // 动画持续时间
    animation.repeatCount = -1; // 重复次数
    animation.autoreverses = YES; // 动画结束时执行逆动画
    
    // 缩放倍数
    animation.fromValue = [NSNumber numberWithFloat:1.0]; // 开始时的倍率
    animation.toValue = [NSNumber numberWithFloat:0.9]; // 结束时的倍率
    
    // 添加动画
    [view.layer addAnimation:animation forKey:@"scale-layer"];
    
}

这里需要注意的是animation的一个属性:fillMode
fillMode的作用就是决定当前对象过了非active时间段的行为. 比如动画开始之前,动画结束之后。如果是一个动画CAAnimation,则需要将其removedOnCompletion设置为NO,要不然fillMode不起作用.
下面来讲各个fillMode的意义

  • kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
  • kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
  • kCAFillModeBackwards 这个和kCAFillModeForwards是相对的,就是在动画开始前,你只要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始.你可以这样设定测试代码,将一个动画加入一个layer的时候延迟5秒执行.然后就会发现在动画没有开始的时候,只要动画被加入了layer,layer便处于动画初始状态
  • kCAFillModeBoth 理解了上面两个,这个就很好理解了,这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态.

CAKeyframeAnimation

/** General keyframe animation class. **/

@interface CAKeyframeAnimation : CAPropertyAnimation

CAKeyframeAnimation不同于basic是吧动画从起点到终点均匀划分到时间片里,而是通过提供一个数值数组

/* An array of objects providing the value of the animation function for
 * each keyframe. */

@property(nullable, copy) NSArray *values;

我们可以将duration中的每一帧数值都提供出来,放在数组里
下面给出示例代码,这里再次感谢Kitten Yang

-(CAKeyframeAnimation *)createSpringAnima:(NSString *)keypath duration:(CFTimeInterval)duration usingSpringWithDamping:(CGFloat)damping initialSpringVelocity:(CGFloat)velocity fromValue:(id)fromValue toValue:(id)toValue{
    
    CGFloat dampingFactor  = 10.0;
    CGFloat velocityFactor = 10.0;
    NSMutableArray *values = [self springAnimationValues:fromValue toValue:toValue usingSpringWithDamping:damping * dampingFactor initialSpringVelocity:velocity * velocityFactor duration:duration];
    
    CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keypath];
    anim.values = values;
    anim.duration = duration;
    anim.fillMode = kCAFillModeForwards;
    anim.removedOnCompletion = NO;
    
    return anim;
}
-(NSMutableArray *) springAnimationValues:(id)fromValue toValue:(id)toValue usingSpringWithDamping:(CGFloat)damping initialSpringVelocity:(CGFloat)velocity duration:(CGFloat)duration{
    
    
    //60个关键帧
    NSInteger numOfFrames  = duration * 60;
    NSMutableArray *values = [NSMutableArray arrayWithCapacity:numOfFrames];
    for (NSInteger i = 0; i < numOfFrames; i++) {
        [values addObject:@(0.0)];
    }
    
    //差值
    CGFloat diff = [toValue floatValue] - [fromValue floatValue];
    
    for (NSInteger frame = 0; frame<numOfFrames; frame++) {
        
        CGFloat x = (CGFloat)frame / (CGFloat)numOfFrames;
        CGFloat value = [toValue floatValue] - diff * (pow(M_E, -damping * x) * cos(velocity * x)); // y = 1-e^{-5x} * cos(30x)
        //Y = a + b *cos(c*x)*e^{-d*x}
        // x=0 ==>a + b= from;
        // x=1 ==>近似忽略冪次项,==》a = to;
        // ==> b = from - to
        // 不准确
        
        values[frame] = @(value);
    }

    return values;
    
}

这里的算法是假设呈衰减的谐振函数
对于如何选择相关参数,可以在这个网站中输入相关函数来观察图象
比如在做手势密码时,做了一个仿iphone的touch ID输错的效果
提示语会来回震动:

-(void)addAlertAnimation{
    float positonX = state.layer.position.x;
    //Spring animation
    CAKeyframeAnimation *anim = [[KYSpringLayerAnimation sharedAnimManager]createSpringAnima:@"position.x" duration:1 usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(positonX-60) toValue:@(positonX)];
    
    //line animation
    //    CAKeyframeAnimation *anim = [[KYSpringLayerAnimation sharedAnimManager] createBasicAnima:@"factor" duration:0.4 fromValue:@(0.5+[howmanydistance floatValue]* 1.5) toValue:@(0)];
    
    //half animation
    //    CAKeyframeAnimation *anim = [[KYSpringLayerAnimation sharedAnimManager] createHalfCurveAnima:@"factor" duration:0.3 fromValue:@(0.5+[howmanydistance floatValue]* 1.5) toValue:@(0)];
    
    anim.delegate = self;
    [state.layer addAnimation:anim forKey:@"shaking"];
    
}

动画过程中改变label的中心位置,这里要选择合适的参数使这个震荡过程至少持续了一个周期,不然抖动只在一侧出现

4. 通过修改layer的相关自有属性

属性变化会触发动画,如:
self.arrowImageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 0, 1);
self.arrowImageView.layer.transform = CATransform3DIdentity;
缩放动画的关键是transform.scale
x轴,y轴同时按比例缩放:

CABasicAnimation *theAnimation;
theAnimation=[CABasicAnimation animationWithKeyPath:@"transform.scale"];
theAnimation.duration=8;
theAnimation.removedOnCompletion = YES;
theAnimation.fromValue = [NSNumber numberWithFloat:1];
theAnimation.toValue = [NSNumber numberWithFloat:0.5];
 [yourView.layer addAnimation:theAnimation forKey:@"animateTransform"];

以上缩放是以view的中心点为中心缩放的,如果需要自定义缩放点,可以设置卯点:
//中心点
[yourView.layer setAnchorPoint:CGPointMake(0.5, 0.5)];

//左上角
[yourView.layer setAnchorPoint:CGPointMake(0, 0)];

//右下角

[yourView.layer setAnchorPoint:CGPointMake(1, 1)];

补充

要做一个动效,需要充分理解position与anchorPoint 。这篇文章写的很不错,其中的几个要点:

  1. position是layer中的anchorPoint在superLayer中的位置坐标。
  2. 互不影响原则:单独修改position与anchorPoint中任何一个属性都不影响另一个属性。
  3. frame、position与anchorPoint有以下关系:
frame.origin.x = position.x - anchorPoint.x * bounds.size.width; 
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;

第2条的互不影响原则还可以这样理解:position与anchorPoint是处于不同坐标空间中的重合点,修改重合点在一个坐标空间的位置不影响该重合点在另一个坐标空间中的位置。但这样会引起frame的变化,若是想改变anchor point后仍维持frame不变,则可以:

- (void) setAnchorPoint:(CGPoint)anchorpoint forView:(UIView *)view
{
CGRect oldFrame = view.frame;
view.layer.anchorPoint = anchorpoint;
view.frame = oldFrame;
}
 重设一下就好了

实现组合动画:

// Animation group
CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil];
group.duration = 5.0;
[myLayer addAnimation:group forKey:@"BorderChanges"];


注意组合动画中默认是并行的,要实现连贯的需要指定动画的起始时间
// animation2.beginTime = CACurrentMediaTime()+ animation1.duration;

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

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,321评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,001评论 5 13
  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 2,969评论 0 21
  • Core Animation其实是一个令人误解的命名。你可能认为它只是用来做动画的,但实际上它是从一个叫做Laye...
    小猫仔阅读 3,545评论 1 4
  • 在iOS实际开发中常用的动画无非是以下四种:UIView动画,核心动画,帧动画,自定义转场动画。 1.UIView...
    请叫我周小帅阅读 3,000评论 1 23