使用CADisplayLink与CAMediaTimingFunction“结合”来为UIView创建动画(以获取任意曲线)。

6
我正在使用一个 CADisplayLink 来执行视图动画,仅插值一个值并重绘视图本身。

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

为了实现动画效果,我使用 CADisplayLink 并希望视图在值之间进行“变形”。我通过从动画的起始值和结束值插值该值来实现:

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

现在获得线性进度很容易,而获得“特定曲线”(好的),但我想能够利用 CAMediaTimingFunction 来实现这一点(或者其他现有逻辑 - 我不想再重新发明轮子 :)。


为什么要使用CADisplayLink?它只是一个计时器。而CAMediaTimingFunction只是存储曲线的一些关键参数,本身并不执行曲线。如果你想要一个完全支持核心动画的自定义属性,就像标准可动画属性一样,那么你应该继承CALayer。请参阅Xcode文档中的“向图层添加自定义属性”。 - SolaWing
1个回答

3

有一个很棒的代码片段RSTiming,它可能对你有用。你可以使用标准的CAMediaTimingFunction定义时间函数,甚至可以使用2个控制点来定义贝塞尔曲线来定义自定义时间函数。

如果我理解你的设置正确,你可能会有类似这样的代码:

ViewController

#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

AnimatedView

#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播放器生成)

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接