使用 CADisplayLink“结合”与 CAMediaTimingFunction 为 UIView 设置动画。 (获得任意曲线)

Posted

技术标签:

【中文标题】使用 CADisplayLink“结合”与 CAMediaTimingFunction 为 UIView 设置动画。 (获得任意曲线)【英文标题】:animate a UIView using CADisplayLink 'combined' with CAMediaTimingFunction. (to get a arbitrary curve) 【发布时间】:2015-04-29 08:48:53 【问题描述】:

我正在使用CADisplayLink 做一个视图动画,它只是插入一个值并重绘视图本身。

例如我有一个视图MyView,它有一个属性value,每当设置值时,我都会调用setNeedsDisplay,视图知道要绘制什么。

为此我使用CADisplayLink 进行动画处理,并且我希望视图在值之间“变形”。我只是通过从动画的开始和停止值中插入值来做到这一点:

- (CGFloat)interpolatedValue:(CGFloat)sourceValue withValue:(CGFloat)targetValue forProgress:(CGFloat)progress;

现在获得线性进展很容易并获得“特定曲线”(好的),但我希望能够利用CAMediaTimingFunction 来做到这一点(或其他一些预先存在的逻辑 - 我不想重新发明再次***':)

【问题讨论】:

为什么要使用 CADisplayLink?这只是一个计时器。而 CAMediaTimingFunction 只是存储曲线的一些关键参数。本身不做曲线。如果你想要的只是一个完全支持 Core Animation 的自定义属性,就像一个标准的动画属性一样,你应该继承 CALayer。请参阅 Xcode 文档中的 Adding Custom Properties to a Layer 【参考方案1】:

有这个很棒的要点RSTiming 在您的情况下可能会很方便。您可以使用标准 CAMediaTimingFunction 定义计时函数,甚至可以使用 2 个控制点定义自定义函数来定义贝塞尔曲线。

如果我得到了你的设置,你可能会有这样的事情:

视图控制器

#import "ViewController.h"
#import "AnimatedView.h"
#include <stdlib.h>

@interface ViewController ()

@property (nonatomic, strong) CADisplayLink *displayLink;
@property (weak, nonatomic) IBOutlet AnimatedView *viewToAnimate;

@end

@implementation ViewController

- (void)viewDidLoad 
    [super viewDidLoad];

    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateFrame)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];


- (void)updateFrame 
    [self.viewToAnimate updateAnimation];


- (IBAction)updateAnimationTapped:(id)sender 
    self.viewToAnimate.value = arc4random_uniform(101) / 100.0;
    NSLog(@"Next animation value: %f", self.viewToAnimate.value);


@end

动画视图

#import "AnimatedView.h"
#import "RSTimingFunction.h"

@interface AnimatedView()

    CGFloat _lastValue;
    CGFloat _progressStep;
    CGFloat _currentProgress;


@property (nonatomic, strong) RSTimingFunction *animationProgress;

@end

@implementation AnimatedView

- (instancetype)initWithCoder:(NSCoder *)aDecoder

    if ((self = [super initWithCoder:aDecoder]))
    
        _progressStep = 0.01; // defines animation speed
        _currentProgress = 1.0;
        self.animationProgress = [RSTimingFunction timingFunctionWithName:kRSTimingFunctionEaseInEaseOut];
    

    return self;


- (void)setValue:(CGFloat)value 
    if (_value != value)
    
        _lastValue = _value;
        _value = value;
        _currentProgress = 0.0;
    


- (void)updateAnimation

    if (_currentProgress > 1.0)
        return;

    _currentProgress += _progressStep;
    CGFloat currentAnimationValue = _lastValue + (_value - _lastValue) * [self.animationProgress valueForX:_currentProgress];

    self.alpha = currentAnimationValue; // as an example animate alpha


@end

如前所述,您甚至可以设置 2 个控制点来创建以三次贝塞尔曲线为模型的计时函数。

self.animationProgress = [RSTimingFunction timingFunctionWithControlPoint1:CGPointMake(0.6, 0.6) controlPoint2:CGPointMake(0.1, 0.8)];

这将产生以下定时动画(使用CAMediaTimingFunction playground制作)

【讨论】:

以上是关于使用 CADisplayLink“结合”与 CAMediaTimingFunction 为 UIView 设置动画。 (获得任意曲线)的主要内容,如果未能解决你的问题,请参考以下文章

iOS CoreAnimation 逐帧动画 CADisplayLink

CADisplaylink初解

iOS 动画篇 CADisplayLink与CoreGraphics实现动画

解决NSTimer或CADisplayLink计时器造成的循环引用问题。

iOS CADisplayLink 定时器的使用

如何用 CADisplayLink 替换 NSTimer?