新动画类

交互动画之 UIViewPropertyAnimator

  • 2017 年 05 月 28 日
  • iOS

本文将介绍新增的交互动画类型 UIViewPropertyAnimator ,以及为什么我们要开始使用它。在 iOS10 之前,animateWithDuration:animations: 函数是 UIView 层级系统动画交互的默认选项。不过这套系统 API 远不能满足当下越来越复杂的交互设计,而 Facebook’s POP 类型的交互框架则成功上位填补了缺口。不过好在 Apple 也注意到了这个问题,并在新系统中带来了更强大的动画 API 。

UIViewPropertyAnimator 与之前的 UIView animations 有很多差异。其中最明显的差异就是它允许你持有对象实例。另外,除了能够对动画过程进行暂停和恢复外,你还以对进行逆转和销毁操作。这些特性极大的拓展了视图动画交互的想象空间。下面我们看个示例:

示例模仿了 Youtube 视频播放时的浮动交互效果,视频既可以全屏也可以在右下角进行播放。用户只需要上下进行拖动就能实现状态的切换。这个动画也是 UIViewPropertyAnimator 一个非常好的应用示例,接下来看看它是如何实现的。

为了弄清楚 UIViewPropertyAnimator 的原理,首先需要了解动画过程中的各种状态:

  • Inactive:对象初始化或者动画结束后所处的状态。
  • Active:在调用 startAnimation() 、 pauseAnimation() 方法后对象就处于激活状态,直到动画完成或者手动调用 stopAnimation() 结束动画。
  • Stopped:在 stopAnimation() 被调用之后动画对象就处于停止状态,并且保留当前的所有属性值。如下图所示,停止状态无法反向回到激活态。

动画属性的修改只能在 Inactive 状态下进行。

下面通过代码介绍 UIViewPropertyAnimator 的使用,先对视图添加手势操作:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [self.view addGestureRecognizer:self.panGestureRecognizer];
}

另外,这里需要一些辅助变量。一个枚举类型用于标记视图的当前状态,并依据状态值决定动画是否反向运行。 创建一个动画示例,并在开始动画之前存储视图的起始 frame 用于反向动画。之所以记录起始 frame 是因为: reversed 属性在一次动画过程中只能设置一次,多次修改会导致动画失效。

typedef NS_ENUM(NSInteger, PlayerState) {
    PlayerStateThumbnail,       // 缩略状态
    PlayerStateFullscreen,          // 全屏状态
};

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIView *playerView;
@property (nonatomic) UIViewPropertyAnimator *playerViewAnimator;
@property (nonatomic) PlayerState playerState;
@property (nonatomic) CGRect originalPlayerViewFrame;
@property (nonatomic) UIPanGestureRecognizer *panGestureRecognizer;

@end

接下来需要讲手势的变化传递到视图并使之做出相关的响应。下面是手势开始时的处理:

- (void)panningBegan {
    if (self.playerViewAnimator.isRunning) {
        return;
    }

    CGRect targetFrame;

    switch (self.playerState) {
        case PlayerStateThumbnail:
            self.originalPlayerViewFrame = self.playerView.frame;
            targetFrame = self.view.frame;
            break;
        case PlayerStateFullscreen:
            targetFrame = self.originalPlayerViewFrame;
            break;
    }

    self.playerViewAnimator = [[UIViewPropertyAnimator alloc] initWithDuration:0.5 dampingRatio:0.8 animations:^{
        self.playerView.frame = targetFrame;
    }];
}

上面的代码中,我们首先确保了当前没有动画在运行中,紧接着根据当前视图状态初始化了目标 frame。接下来,我们需要处理手指持续移动时的动画状态:

- (void)panningChangedWithTranslation:(CGPoint)translation {

    if (self.playerViewAnimator.isRunning) {
        return;
    }

    CGFloat translatedY = self.view.center.y + translation.y;

    CGFloat progress = 0.001 ;
    switch (self.playerState) {
        case PlayerStateThumbnail:
            progress = 1 - (translatedY / self.view.center.y);
            break;
        case PlayerStateFullscreen:
            progress = (translatedY / self.view.center.y) - 1;
    }

    progress = MAX(0.001, MIN(0.999, progress));

    self.playerViewAnimator.fractionComplete = progress;
}

这里计算了不同情形下移动量所占的对应比例,并且将其赋值给动画中的 fractionComplete属性。

现在,当用户上下移动手指的时视图已经能够顺利的切换到相应的状态。但是,用户松开手指的时候也能的意愿并不一定是状态切换也可能会是取消动画回到起始的状态。所以,这里应该存在一些阀值和属性判断完善用户体验。

下面就使用手势里常见的位移和速度来充当:

- (void)panningEndedWithTranslation:(CGPoint)translation velocity:(CGPoint)velocity {

    self.panGestureRecognizer.enabled = NO;

    CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
    __weak ViewController *weakSelf = self;

    switch (self.playerState) {
        case PlayerStateThumbnail:
            if (translation.y <= -screenHeight / 3 || velocity.y <= -100) {
                self.playerViewAnimator.reversed = NO;
                [self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
                    weakSelf.playerState = PlayerStateFullscreen;
                    weakSelf.panGestureRecognizer.enabled = YES;
                }];
            } else {
                self.playerViewAnimator.reversed = YES;
                [self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
                    weakSelf.playerState = PlayerStateThumbnail;
                    weakSelf.panGestureRecognizer.enabled = YES;
                }];
            }
            break;
        case PlayerStateFullscreen:
            if (translation.y >= screenHeight / 3 || velocity.y >= 100) {
                self.playerViewAnimator.reversed = NO;
                [self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
                    weakSelf.playerState = PlayerStateThumbnail;
                    weakSelf.panGestureRecognizer.enabled = YES;
                }];
            } else {
                self.playerViewAnimator.reversed = YES;
                [self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
                    weakSelf.playerState = PlayerStateFullscreen;
                    weakSelf.panGestureRecognizer.enabled = YES;
                }];
            }
            break;
    }

    CGVector velocityVector = CGVectorMake(velocity.x / 100, velocity.y / 100);
    UISpringTimingParameters *springParameters = [[UISpringTimingParameters alloc] initWithDampingRatio:0.8 initialVelocity:velocityVector];

    [self.playerViewAnimator continueAnimationWithTimingParameters:springParameters durationFactor:1.0];
}

如果当前播放视图的高度超过了 1/3 或者手指松开时的速度够快的话,状态切换动画会继续进行下去。否则,视图动画会反向进行恢复到原有状态。值得注意的是,UIViewPropertyAnimator 实例对象可以动态的添加多个 animation block、completion block,哪怕此时动画正在运行中。

最后,我们需要将上面的处理函数与手势状态关联起来:

- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
    CGPoint translation = [recognizer translationInView:self.view.superview];

    if (recognizer.state == UIGestureRecognizerStateBegan) {
        [self panningBegan];
    }

    if (recognizer.state == UIGestureRecognizerStateEnded) {
        CGPoint velocity = [recognizer velocityInView:self.view];
        [self panningEndedWithTranslation:translation velocity:velocity];
    } else {
        CGPoint translation = [recognizer translationInView:self.view.superview];
        [self panningChangedWithTranslation:translation];
    }
}

文章到此为止,相信你对这个新交互动画 API 已经有了基本的了解。

时间: 2024-11-07 11:09:11

新动画类的相关文章

扩展 WPF 动画类

原文:扩展 WPF 动画类 扩展 WPF 动画类                                                                     Charles Petzold                                                                     http://msdn.microsoft.com/msdnmag/issues/07/07/Foundations/Default.aspx?l

Unity3d 4.0新动画系统Mecanim用法(二)

上一篇,我们初步了解了一下Mecanim的部分很基础的类容,我以一个疑问的形式结尾.这次我来揭晓此问题的答案,其实很简单,上次的警告如下: 4.0新动画系统Mecanim用法(二)"> 警告的大概意思是:用在Animator Controller中的Animation clips需要有在检视面板中被设置了Muscle(肌肉)的这个步骤. 我的英文很烂,但我可以知道这句话的含义,就是我们的用到的这个Animation Clip必须是已经产生了Avatar的模型中的Animation Clip

【Android】详解7.0带来的新工具类:DiffUtil

一 概述 DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集->新数据集的最小变化量. 说到数据集,相信大家知道它是和谁相关的了,就是我的最爱,RecyclerView. 就我使用的这几天来看,它最大的用处就是在RecyclerView刷新时,不再无脑mAdapter.notifyDataSetChanged(). 以前无脑mAdapter.notifyDataSetChanged()有两个缺点: 不会触发RecyclerView的动画(删除.

cocos2d 播放GIF动画类

cocos2d 播放GIF动画类 以前项目中曾经用到过,后来因为GIF图像的质量较差,被弃用了,把公司名字去掉分享下,根据网上资料改编的cocos2d-iphone版的. // // CCSpriteGif.h // // Created by Yuming on 13-1-23. // Copyright 2013年 __MyCompanyName__. All rights reserved. // // 本类需要导入ImageIO.framework #import <Foundation

动画类型和时间指南(动画类结构图)

Core Animation 提供了一套非常科学的动画类,我们能够在程序中使用. CAAnimation 是所以动画类的虚基类.它遵循CAMediaTiming 协议,CAMediaTiming 协议为动画提供了简单的持续时间(duration).数度(speed).重复次数(repeat count).CAAnimation 还遵循了CAAction 协议,这个协议提供了标准化的定义在响应层(layer)触发动作时开启动画. CAAnimation 也定义了动画计时作为CAMediaTimin

android ScaleAnimation类:尺寸变化动画类

Android JDK为我们提供了4种动画效果,分别是: AlphaAnimation,RotateAnimation, ScaleAnimation, TranslateAnimation.今天我想讲解的是TranslateAnimation这个动画效果.也是本人在做一个移动图片的动画效果的项目时,遇到了一些问题.在网上查了很多资料,搞了好几天.终于明白怎么使用这个TranslateAnimation,在本文中记录下来,以便以后忘记了可以查阅. http://gundumw100.iteye.

ScaleAnimation类:尺寸变化动画类

9.4  ScaleAnimation类:尺寸变化动画类 ScaleAnimation类是Android系统中的尺寸变化动画类,用于控制View对象的尺寸变化,该类继承于Animation类.ScaleAnimation类中的非常多方法都与Animation类一致,该类中最经常使用的方法便是ScaleAnimation构造方法. [基本的语法]public ScaleAnimation (float fromX, float toX, float fromY, float toY, int pi

(六)Unity5.0新特性------新动画功能

?? unity 5.0 中的新动画功能 这里是你可以期待的新动画功能快速概述 ! State Machine Behaviours状态机行为 在Unity 5 中,你会能够将StateMachineBehaviour 脚本添加到您的states,当played状态时能接收callbacks回调: ?OnStateEnter ?OnStateUpdate ?OnStateExit ?OnStateMove ?OnStateIK 在你的状态,您可以创建尽可能多的StateMachineBehavi

TranslateAnimation类:位置变化动画类 (类似tab切换效果)

TranslateAnimation类是Android系统中的位置变化动画类,用于控制View对象的位置变化,该类继承于Animation类.TranslateAnimation类中的很多方法都与Animation类一致,该类中最常用的方法便是TranslateAnimation构造方法. [基本语法]public TranslateAnimation (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) 参数说