iOS 动画篇 - pop动画库

pop

Pop 是 iOS,tvOS 和 OS X 的可扩展动画引擎。除了基本的静态动画外,他支持弹性和衰减动画动态动画,使其可用于构建逼真的基于物理学的交互。API 允许与现有的 Objective-C 或 Swift 代码库快速集成,并支持 NSObject 上任何属性的动画。它是一个成熟并且经过良好测试的框架。

本文主要着重介绍 Pop 库的使用,并结合实例作相关动画的演示。

简单示例

我们先看来一下一个简单的示例:移动一个视图的 center。

// 选择动画类型,并指定需要动画的视图view属性或图层layer属性
POPBasicAnimation* animation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter];
// 指定属性的新值
animation.toValue = [NSValue valueWithCGPoint:self.view.center];
// 设置动画的事件回调
animation.delegate = self;
// 向视图view或图层layer添加动画
[self.blueView pop_addAnimation:animation forKey:@"popViewCenterAnimation"];

移动视图

通过上面的例子,我们发现使用 pop 作动画的过程和 Core Animation 没多少区别,这让我们很容易就能上手。另外,我们注意到,使用 pop 主要有以下几个步骤:

  1. 选择动画类型,并指定需动画的属性
  2. 执行属性的新值
  3. 设置动画事件代理(可选)
  4. 应用动画

动画介绍

Pop 是使用 Objective-C++ 编写的,该语言是对 C++ 的扩展,就像Objective-C 是 C 的扩展。而至于为什么他们用 Objective-C++ 而不是纯粹的 Objective-C,原因是他们更喜欢 Objective-C++的语法特性所提供的便利。

Pop 支持四种动画类型:

  • POPBasicAnimation(基础动画)
  • POPSpringAnimation(弹性动画)
  • POPDecayAnimation(衰减动画)
  • POPCustomAnimation(自定义动画)

我们知道 Core Animation 针对的图层级别的变换,并且变化之后视图的 frame 以及图层的 frame 都不会变化(即使设置 removedOnCompletion 设置为NO,不移除动画,fillMode 填充模式设置为 kCAFillModeForwards)。和 Core Animation 不同的,Pop 所做的动画都真实的改变了 frame,并且都会保持最后一帧的位置。另外一个不同点是,Pop 直接列举了所有可以做的属性动画,并且所针对的对象不再只是图层 CALayer,是视图 UIView 也进行了支持。

动画结构

动画结构

基础动画

在文章开始的例子就使用了基础动画。这里介绍一下其中的属性和方法。

POPBasicAnimation

  • 初始化

POPBasicAnimation 提供了多种初始化基础动画对象的方法,其中包括无任何设置的动画对象方法、指定动画属性的方法、指定时间速率的方法:

+animation

+animationWithPropertyNamed:

+defaultAnimation

+linearAnimation

+easeInAnimation

+easeOutAnimation

+easeInEaseOutAnimation

  • duration

动画执行的时间,单位为秒,默认为 0.5 秒。

  • timingFunction

时间速率,是系统的 CAMediaTimingFunction 类型,支持系统提供的五种类型,对应上面的快捷创建方式。通常用在指定动画属性之后来指定运行的速率。

POPPropertyAnimation 和 POPAnimation

此部分是几种动画的父类,在基础动画、弹性动画和衰减动画中都可以使用。

  • property:用来设置动画属性
  • fromValue:动画的起始值
  • toValue:动画的起始值
  • name:动画的名称
  • beginTime:和核心动画中一样,动画的开始时间
  • delegate:动画过程事件的代理,后面会提到
  • tracer:跟踪器,收集动画信息,调试时使用的。
  • removedOnCompletion:和核心动画中一样,动画结束是否移除动画
  • autoreverses:反转动画
  • repeatCount:重复次数,0和1表示不重复
  • repeatForever:无限次的重复,设置为YES,动画不会执行完成事件

示例:我们来继续之前移动视图的例子,不同的是我们设置了时间、速率曲线、重复等效果,并且特地使用核心动画写了一个类似的对比动画。

// pop 动画
POPBasicAnimation* animation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition];
animation.toValue = [NSValue valueWithCGPoint:self.view.center];
// 设置动画时间
animation.duration = 1.0;
// 设置动画速率
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
// 设置重复次数
animation.repeatForever = YES;
// 设置动画反转
animation.autoreverses = YES;
[self.blueView.layer pop_addAnimation:animation forKey:@"POPBasicAnimation"];

// Core Animation 动画
CABasicAnimation* ani = [CABasicAnimation animationWithKeyPath:@"position"];
ani.toValue = [NSValue valueWithCGPoint:CGPointMake(self.view.center.x, self.view.center.y+60)];
ani.duration = 1.0;
ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
ani.repeatCount = MAXFLOAT;
ani.autoreverses = YES;
[self.blueView2.layer addAnimation:ani forKey:@"CABasicAnimation"];

两种不同的基础动画效果

可以看出,Pop 动画和核心动画在相同的时间速率曲线设置下,运行起来效果还是有细微的差别的。对比了其他几个速率类型,个人觉得还是核心动画的效果要好。

弹性动画

POPSpringAnimation 可以用来做弹性动画,和基础动画不同,弹性动画不能指定速率曲线,也没有动画时间一说,因为弹性动画是由初始速度、弹性能力等多个参数决定的其效果的。

  • velocity:当前的速度值
  • springBounciness:有效弹性,值会被转换为相应的动力学常数。较高的值会增加弹簧移动范围,从而产生更多的振荡和弹性。定义为[0,20]范围内的值。 默认为4。
  • springSpeed:有效速度,较高的值会增加弹簧的阻尼能力,从而导致更快的初始速度和更快的弹跳速度。定义为[0,20]范围内的值。默认为12。
  • dynamicsTension:动力学中使用的张力。可以在弹跳和速度上使用,以便更精细地调整动画效果。
  • dynamicsFriction:动力学中使用的摩擦力。可以在弹跳和速度上使用,以便更精细地调整动画效果。
  • dynamicsMass:动力学中使用的质量。可以在弹跳和速度上使用,以便更精细地调整动画效果。

示例:

POPSpringAnimation* animation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionX];
animation.toValue = @(self.view.center.x);
// 设置弹力
animation.springBounciness = 20.0f;
[self.blueView.layer pop_addAnimation:animation forKey:@"POPSpringAnimation"];

弹性动画

衰减动画

和弹性动画一样,衰减动画同样无法指定动画的时间,因为动画是根据初始速度和衰减因子计算得出动画时间的。另外,你也无法设置toValue的值,因为此值是由速度和衰减因子计算得出来的,不过,你可以设置fromValue指定动画的起点。

  • velocity:只读,动画的初始速度,此速度在动画执行期间会不断变换
  • originalVelocity:因为velocity是不断变化的,因此 pop 提供了一个原始速度,记录着动画的初始速度
  • deceleration:衰减因子,范围[0,1],默认值为0.998
  • duration:只读,动画时间,初始速度和衰减因子计算得出

示例:

POPDecayAnimation* animation = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX];
// 设置初始速度
animation.velocity = @(300);
[self.blueView.layer pop_addAnimation:animation forKey:@"POPDecayAnimation"];

衰减动画

自定义动画

POPCustomAnimation 类,它基本上是一个 display link 的转换,它会在每一帧进行回调,以此让你来完成动画每一帧的过程。

POPCustomAnimation 类中有一个 POPCustomAnimationBlock 回调,它包含了所添加动画的视图和当前动画,它需要返回值,YES表示继续执行,此时你可以继续操作你的视图,而 NO 则表示动画结束,POPCustomAnimationBlock 便不会再回调。

想要自定义动画我们就需要有一个自定义的函数曲线,比如我们要实现一个弹簧动画(跟spring动画类似),我们使用如下的时间函数,输出为[0-1](更多的缓动函数可以去这查看:http://easings.net)。

float ElasticEaseOut(float p)
{
    return sin(-13 * M_PI_2 * (p + 1)) * pow(2, -6 * p) + 1;
}

当有了定义好的缓动曲线后,我们就可以实现自定义动画。

@interface ViewController ()
@property(assign, nonatomic)CGFloat baseTime;
@property(assign, nonatomic)CGFloat duration;
@property(assign, nonatomic)CGPoint from;
@property(assign, nonatomic)CGPoint to;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.from = self.blueView.center;
    self.to = self.view.center;
    self.duration = 2.0; // 动画总时长
}

float ElasticEaseOut(float p){
    return sin(-13 * M_PI_2 * (p + 1)) * pow(2, -6 * p) + 1;
}

-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    POPCustomAnimation* animation = [POPCustomAnimation animationWithBlock:^BOOL(id target, POPCustomAnimation *animation) {
        //动画开始的时间,我们可以记录下来作为基准时间
        if(self.baseTime == 0){
            self.baseTime = animation.currentTime;
        }
        
        // 根据当前时间,计算出当前的时间进度,并根据动画周期归一化到[0-1]
        double progress = (animation.currentTime - self.baseTime)/self.duration;
        
        //使用ElasticEaseOut自定义曲线根据当前进度计算出新的值,该值大小也为[0-1]
        double caculateValue = ElasticEaseOut(progress);
        
        //根据缓动函数的输出,计算新的值,并赋给UI对象
        CGPoint current = CGPointZero;
        current.x = self.from.x + (self.to.x - self.from.x) * caculateValue;
        current.y = self.from.y + (self.to.y - self.from.y) * caculateValue;
        self.blueView.center = current;
        
        //如果当前进度小于1,则继续动画
        if(progress < 1.0){
            return YES;
        }
        // 结束动画
        return NO;
    }];
    [self.blueView.layer pop_addAnimation:animation forKey:@"POPCustomAnimation"];
}

动画效果和Spring类似

可选择的动画属性
图层 CALayer释义
kPOPLayerBackgroundColor背景色
kPOPLayerBounds大小
kPOPLayerCornerRadius圆角大小
kPOPLayerBorderWidth宽度
kPOPLayerBorderColor边框颜色
kPOPLayerOpacity不透明度
kPOPLayerPosition位置
kPOPLayerPositionX位置x
kPOPLayerPositionY位置y
kPOPLayerRotation旋转
kPOPLayerRotationXX轴旋转量
kPOPLayerRotationYX轴旋转量
kPOPLayerScaleXX轴缩放量
kPOPLayerScaleXYXY轴缩放量
kPOPLayerScaleYXY轴缩放量
kPOPLayerSize宽高的大小
kPOPLayerSubscaleXY
kPOPLayerSubtranslationX
kPOPLayerSubtranslationXY
kPOPLayerSubtranslationY
kPOPLayerSubtranslationZ
kPOPLayerTranslationXX轴平移量
kPOPLayerTranslationXYXY轴平移量
kPOPLayerTranslationYY轴平移量
kPOPLayerTranslationZZ轴平移量
kPOPLayerZPosition
kPOPLayerShadowColor阴影颜色
kPOPLayerShadowOffset阴影偏移大小
kPOPLayerShadowOpacity阴影不透明度
kPOPLayerShadowRadius阴影的圆角
图层 CAShapeLayer释义
kPOPShapeLayerStrokeStart起点端
kPOPShapeLayerStrokeEnd终点端
kPOPShapeLayerStrokeColor线条颜色
kPOPShapeLayerFillColor填充色
kPOPShapeLayerLineWidth线条的宽度(粗细)
kPOPShapeLayerLineDashPhase
NSLayoutConstraint释义
kPOPLayoutConstraintConstant约束值
视图 UIView释义
kPOPViewAlpha透明度
kPOPViewBackgroundColor背景色
kPOPViewBounds大小
kPOPViewCenter中心点
kPOPViewFrame位置和大小
kPOPViewScaleXX轴的缩放量
kPOPViewScaleXYXY轴的缩放量
kPOPViewScaleYY轴的缩放量
kPOPViewSize大小(比例)
kPOPViewTintColor
滚动视图 UIScrollView释义
kPOPScrollViewContentOffset内容滚动位置
kPOPScrollViewContentSize内容大小
kPOPScrollViewZoomScale缩放大小
kPOPScrollViewContentInset内边距
kPOPScrollViewScrollIndicatorInsets指示器内边距
列表视图 UITableView释义
kPOPTableViewContentOffset内容滚动位置
kPOPTableViewContentSize内容大小
集合视图 UICollectionView释义
kPOPCollectionViewContentOffset内容滚动位置
kPOPCollectionViewContentSize内容大小
导航视图 UINavigationBar释义
kPOPNavigationBarBarTintColor着色
工具栏视图 UIToolbar释义
kPOPToolbarBarTintColor着色
导航视图 UITabBar释义
kPOPTabBarBarTintColor着色
标签视图 UILabel释义
kPOPLabelTextColor文字颜色
操作动画

和核心动画一样,Pop 的动画提供了一样的操作动画的方式,你可以添加、删除、获取动画。在默认情况下,Pop 的动画执行完动画之后就被移除了,如果你设置动画的属性removedOnCompletion为NO的情况下,动画将被保留。

  • -pop_addAnimation:forKey::添加动画,这里的 key 用来标识动画身份,后面如果需要获取该动画就有它的用处了。
  • -pop_removeAllAnimations:移除对象上所有动画
  • -pop_removeAnimationForKey::通过 key 移除对象上指定动画
  • -pop_animationKeys:获取对象上所有的动画
  • -pop_animationForKey:通过 key 获取对象上指定动画

动画过程事件

在核心动画中,你可以通过设置动画代理来获取到动画开始和结束事件。同样的,你可以设置 Pop 动画的代理以此来获取动画的开始和结束。除此之外,Pop 动画还提供了另外两个事件–到达或超出预期值以及动画过程中的每一帧。

  • -pop_animationDidStart::动画开始事件
  • -pop_animationDidStop:finished::动画结束事件
  • -pop_animationDidReachToValue::动画到达或超出预期值
  • -pop_animationDidApply::动画过程中每一帧的更新都会调用

在核心动画中,你可能只能通过代理的方式获取到动画的过程事件,就像上面那种,但是这种方式的缺点就是一旦动画繁多时,就要做特别的区分。在 UIView 动画块中,似乎就意识到代理方法的缺陷,采用了block块的形式来回调动画过程事件,这让我们可以一个动画对应一个动画事件。

Pop 动画库中,不仅提供了代理模式的事件回调,而且也提供了block块的回调方法。

@property (copy, nonatomic) void (^animationDidStartBlock)(POPAnimation *anim);
@property (copy, nonatomic) void (^animationDidReachToValueBlock)(POPAnimation *anim);
@property (copy, nonatomic) void (^completionBlock)(POPAnimation *anim, BOOL finished);
@property (copy, nonatomic) void (^animationDidApplyBlock)(POPAnimation *anim);

动画的实例

本节通过几个小示例来演示 Pop 动画的组合使用。

  • 列表的开合

效果

我们要实现上面那种样式的效果。仔细观察动图,你就可以发现,这种效果是使用了弹性动画和基础动画而已。总共有三根线,顶部和底部的线做了中心点位置的移动以及进行了旋转,中线仅仅做了显示隐藏动画。接下来,我们来实现这种效果。

首先是三根线,建议使用图层 CALayer 来做线条。排版的方式很多种,你可以将其放在一个视图中,例如 UIButton。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zoKHQHm5-1586482717681)(https://ws3.sinaimg.cn/large/006tNbRwgy1fxwt6wcvv3j304g03c0sh.jpg)]

我们来做第一根线条的动画,之前说了顶部的线条有两种动画,一种是旋转另一种是中心点的位移。

旋转动画

POPSpringAnimation *transformTopAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerRotation];
transformTopAnimation.toValue = @(M_PI_4);
transformTopAnimation.springBounciness = 20.f;
transformTopAnimation.springSpeed = 20;
transformTopAnimation.dynamicsTension = 1000;
[self.topLayer pop_addAnimation:transformTopAnimation forKey:@"rotateTopAnimation"];

旋转动画

移动中心点动画

CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
POPBasicAnimation *positionTopAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition];
positionTopAnimation.toValue = [NSValue valueWithCGPoint:center];
positionTopAnimation.duration = 0.3;
[self.topLayer pop_addAnimation:positionTopAnimation forKey:@"positionTopAnimation"];

移动中心点动画

同样的,我们给底部线条加上类似的动画效果,当然,旋转的方向和顶部的是相反的。加完之后的效果如下图:

底部视图的动画设置

接下来就是对中间视图的隐藏动画了。

POPBasicAnimation *fadeAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
fadeAnimation.toValue = @(0);
fadeAnimation.duration = 0.3;
[self.middleLayer pop_addAnimation:fadeAnimation forKey:@"fadeAnimation"];

此时,动画效果已经基本完成。
动画效果

至此我们已经完成了闭合效果,接下来要做的就是再次展开效果。使用的动画基本一致,只是该效果的思路和之前完全的相反的。大家可以自行添加尝试。

  • 衰减的球

上面的例子演示了基础动画和弹性动画的结合使用。Pop 动画还有一个衰减动画,为了演示衰减动画效果,这次我们通过手势拖拽一个小球,松手之后让其动过衰减动画自行停止。

首先依旧是前期的准备,我们定义一个视图,并为其添加上拖拽手势,在我们没有添加任何动画的时候是下面的效果:

- (void)handlePan:(UIPanGestureRecognizer *)recognizer{
    CGPoint translation = [recognizer translationInView:self.view];
    recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
                                         recognizer.view.center.y + translation.y);
    [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];

未添加动画

这次我们补上衰减动画

- (void)handlePan:(UIPanGestureRecognizer *)recognizer{
    CGPoint translation = [recognizer translationInView:self.view];
    recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
                                         recognizer.view.center.y + translation.y);
    [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];

    if(recognizer.state == UIGestureRecognizerStateEnded) {
        CGPoint velocity = [recognizer velocityInView:self.view];
        POPDecayAnimation *positionAnimation = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPosition];
        positionAnimation.velocity = [NSValue valueWithCGPoint:velocity];
        [recognizer.view.layer pop_addAnimation:positionAnimation forKey:@"layerPositionAnimation"];
    }
}

衰减效果

总结

pop 动画和 Core Animation 动画的使用上非常类似,pop 动画针对的是视图和图层的属性进行动画,它列举了所有可动画的属性。pop 沿用了 Core Animaton 的部分类型,新增了衰减动画和自定义动画,其中自定动画需要开发者对动画进行每一帧的控制。另一一点,pop 动画针对视图,所有的改变都是真实的。


相关阅读

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值