更改正在运行的动画的持续时间(速度)

时间:2014-02-05 22:01:25

标签: ios objective-c animation uiviewanimation

我正在做一个无限制的旋转动画,当我第一次启动它时效果非常好。我想要实现的是能够在运行时更改旋转速度。我在animationView中有这个功能:

-(void)startBlobAnimation:(float)deltaT
{
    [UIView beginAnimations:@"Spinning" context:nil];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    [UIView setAnimationDuration:deltaT];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationRepeatCount:FLT_MAX];

    CGAffineTransform rotation = CGAffineTransformMakeRotation(-symmetryAngle);
    blobView.transform = rotation;

    // Commit the changes and perform the animation.
    [UIView commitAnimations];
}

首次启动动画后,使用不同的deltaT值调用它不会产生任何影响。如果我在函数的开头添加[wheelView.layer removeAllAnimations];,那么它会成功停止动画,但不会重新启动它。我也尝试使用block命令来启动动画,结果相同。在这一点上我完全感到困惑。有人可以解释问题是什么吗?谢谢!

3 个答案:

答案 0 :(得分:1)

经过长时间的努力,我想出了一个似乎完美无缺的解决方案,并在动画之间实现平滑过渡。基本上我只是弄清楚当前的旋转角度并使用它以不同的速率重新启动动画。这里的一个关键点是在最后一行:你必须有那个anim.keyPath - 它不能是Nil(从经验中学习)。这就是新的动画取代旧动画,我想。哦,为了使它更清晰:symmetryAngle是一个旋转,使对象看起来相同,如72度,5倍对称。

-(void)startWheelsAnimation:(float)deltaT
{
    float startingAngle = 0.0;

    if(isAnimating) {
        // If animation is in progress then calculate startingAngle to
        // reflect the current angle of rotation
        CALayer *presLayer = (CALayer*)[blobView.layer presentationLayer];
        CATransform3D transform = [presLayer transform];
        startingAngle = atan2(transform.m12, transform.m11);
    }

    isAnimating = YES;

    // Restart the animation with different duration, and so that it starts
    // from the current angle of rotation
    CABasicAnimation * anim = [ CABasicAnimation animationWithKeyPath:@"transform.rotation.z" ] ;
    anim.duration = deltaT;
    anim.repeatCount = CGFLOAT_MAX;
    anim.fromValue = @(startingAngle);
    anim.toValue = @(startingAngle - symmetryAngle) ;
    [blobView.layer addAnimation:anim forKey:anim.keyPath];
}

答案 1 :(得分:1)

2017

更改CA动画正在运行的速度。

这就是我如何做“Alex方法”......

我有一个形状图层......

@IBDesignable
class SpinningThing: UIView {

    public func runAnimation() { // call this from the view controller, say
        speedy = 1.0
        startFastThenSlowDown()
    }


    override func layoutSubviews() {

        super.layoutSubviews()
        setup()
    }

    func setup() {

        setup .. other stuff (draw things)
        setupThingThatSpins()
    }

    var thingThatSpins = CAShapeLayer()

    var speedy = 0.5        // start fast
    var finalSpeed = 13.0   // final slow speed

    func setupThingThatSpins() {

        let p = UIBezierPath( .. etc etc
        thingThatSpins.strokeColor .. etc etc
        // so actually draw the thingThatSpins
    }

以下是startFastThenSlowDown ...

中发生的情况
func startFastThenSlowDown() {

    speedy *= 1.175
    animeSpinRestartable(spr: speedy)
    if speedy < finalSpeed {
        delay(0.1) { self._slowDown() }
    }
}

现在,感谢Alex ......

,这是一个可重启的动画
func animeSpinRestartable(spr: CFTimeInterval) {

    let _key = "animeSpinRestartable"

    thingThatSpins.removeAnimation(forKey: _key)
    // is safe the first time through

    // get the current angle...
    let t3 = arcs.presentation()?.transform
    let cur = (t3 == nil) ? 0 : atan2(t3!.m12, t3!.m11)

    let r = _typicalAnim(keyPath: "transform.rotation.z")
    r.fromValue = cur
    r.toValue = cur + 2 * CGFloat.pi
    r.duration = spr

    ///r.isCumulative = true ?  seems unnecessary, so don't do it

    thingThatSpins.add(r, forKey: _key)
}

在我的情况下,thingThatSpins是一个面具(为了善良),它是在其他东西之上(颜色渐变,本身就是动画!)

好的,OP。

答案 2 :(得分:-1)

一些问题 -

  • 我认为对于无尽的动画,你必须直接使用CoreAnimation API。 (这是不正确的,你可以使用UIViewAnimationOptionRepeat)

  • 动画被复制到它们所应用的图层,因此您不能在此之后更改它们(但您可以替换它们,但可能会发现不连续)

  • 您可以设置layer.speed以更改有效动画率

这是一个有效的例子。在Xcode中创建一个新的“空”iOS项目,并像这样替换你的AppDelegate:

@interface AppDelegate ()
@property ( nonatomic, strong ) UIView * animationView ;
@end

@implementation AppDelegate

-(void)buttonClicked
{
    self.animationView.layer.speed = 3.0 - self.animationView.layer.speed ;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    CGRect bounds = self.window.bounds ;
    {
        UIView * subview = [ [ UIView alloc ] initWithFrame:(CGRect){ .size = { 100, 100 } } ] ;
        [ self.window addSubview:subview ] ;

        subview.backgroundColor = [ UIColor orangeColor ] ;
        subview.center = (CGPoint){ CGRectGetMidX( bounds ), CGRectGetMidY( bounds ) } ;

        CABasicAnimation * anim = [ CABasicAnimation animationWithKeyPath:@"transform.rotation.z" ] ;
        anim.duration = 1.0 ;
        anim.repeatCount = CGFLOAT_MAX ;
        anim.fromValue = @0.0 ;
        anim.toValue = @(2.0*M_PI) ;

        [ subview.layer addAnimation:anim forKey:nil ] ;

        self.animationView = subview ;
    }

    UIControl * button = [[ UIControl alloc ] initWithFrame:bounds ] ;
    [ button addTarget:self action:@selector( buttonClicked ) forControlEvents:UIControlEventTouchUpInside ] ;

    [ self.window addSubview:button ] ;


    return YES;
}

@end