2011-05-18 55 views
16

当我的应用程序离开动画停止的背景时。这是正常的。但我想从当前状态重新开始我的动画。我如何做到这一点,而不是在我所有的地方啪啪啪响。iOS,当从背景走出时重新启动动画

[UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{ 
    [bg setFrame:CGRectMake(0, 0, 1378, 1005)]; 
} completion:nil]; 

我试着在动画前面放置一个设置的框架,但这只是让它很快。

[bg setFrame:CGRectMake(0, 0, 1378, 1005)]; 

有什么想法吗?

+0

嗨。你能否解释一下你期望从代码中得到什么样的效果?我认为“当前状态”是当前正在运行(正在进行)动画的状态。我想CA Framework不关心记住动画中断的状态。你是否试图编写一个循环动画,可以停止并从停止的位置再次重新启动,而不回放到初始位置? – 2011-05-18 10:08:04

+0

嗨。这是一种肯恩烧伤效应。 – Stultus 2011-05-18 10:36:08

+0

并且当前状态是当前对象所处的状态。因此它考虑了当前在对象上活动的所有动画 – Stultus 2011-05-18 10:43:44

回答

25

以及@ dany_23的答案可以工作。

但是我发现了另一种方法,如果您不需要恢复动画,但是重新启动动画,并且在重新激活应用程序时没有视图或图层捕捉,它就可以正常工作。

- (void)applicationWillResignActive:(UIApplication *)application 

你叫你的视图 - 控制的方法,它实现下面的代码。

[view.layer removeAllAnimations]; 
// this following CGRect is the point where your view originally started 
[bg setFrame:CGRectMake(0, 0, 1378, 1005)]; 

,并在

- (void)applicationDidBecomeActive:(UIApplication *)application 

你叫你的ViewController的方法,只是启动动画。 像

[UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{ 
     [bg setFrame:CGRectMake(0, 0, 1378, 1005)]; 
} completion:nil]; 

希望这会有所帮助,感谢所有谁回答。

+2

+ 1得到一个清晰明确的答复。 – 2011-05-19 05:12:48

+0

在iOS 7中似乎不再适用。 – openfrog 2013-09-19 20:12:55

+1

http://stackoverflow.com/a/11928261/173190 – 2013-10-10 08:15:43

-1

我不记得确切的细节,但我认为你可以通过查看视图的图层边界和位置属性来获取框架的当前动画位置。您可以将应用程序暂停存储,并在应用程序再次处于前台时进行还原。

3

当你的应用进入后台时,你必须暂停动画,并在它再次变为活动状态时重新开始。您可以找到一些示例代码here,它描述了如何暂停和恢复动画。

2

那么如果我理解正确的问题的方式,你试图创建一个动画,并停止在中间。

在这种情况下,我建议你使用某种“状态”变量来存储动画的状态(例如框架属性)。这个变量可以用于进一步的代码中,将正在动画的对象放在正确的位置。这可以在animationDidStop函数(animationDidStop:finished:)中完成。为了实现这个功能,你将不得不使用委托。它意味着将AppNameViewController(或整个应用程序的其他独特对象)设置为动画的委托,并将实现写入其m文件。这将允许您在动画结束时运行“位置设置”代码。

下一个任务是存储动画状态。在运行动画时,Core Animation框架会创建一堆中间层(表示层)。这些图层在动画过程中显示,然后被删除。当动画完成执行时,对象只会进入最终状态。实际上,当你创建所谓的“显式动画”时,你必须设置它(如果不这样做,动画将播放,但对象将最终跳回)。每个表示层都有一组或所有属性的副本,称为“动画属性”。当其中一个动画属性设置为动画的关键点时,Core Animation调用(BOOL)needsDisplayForKey返回YES/NO。它告诉Core Animation对指定键的更改是否需要重新显示图层。 “重新显示”意味着为表示层调用drawInContext方法。在这里,您可以获取当前显示的表示层的属性,并将其置于“状态”变量中。

动画在线程中播放,并为了设置我使用委托的“状态”变量。它需要继承CALayer(让我们说AnimLayer),并用一种​​存储“状态”的方法定义一个协议。这个方法有一个参数就是状态。然后我实现了将状态存储在AnimLayer类中的协议方法,并将唯一对象设置为委托。所以这些表示层(AnimLayer副本)不会自己存储状态。相反,作为参数传递给委托函数的“状态”值由主线程中的唯一对象存储。

我做了这样的事情,但我的任务有点不同。我不知道更简单的方法,对不起。希望这可以帮助。

+0

+1捕获基本想法。尽管在这种情况下,@Stultus不需要设置委托并在'animationDidStop:finished:'中进行编码。他可以实现'animateWithDuration:delay:options:animations:finished:'方法的'finish:'块。该块有一个“完成”参数,告诉他动画是否被中断。其余的应该像你提到的。 – 2011-05-18 11:28:21

26

你可以在你的类添加一个观察员UIApplicationWillEnterForegroundNotification:

- (void)addNotifications { 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; 
} 

- (void)applicationWillEnterForeground { 
    [self animate]; 
} 

- (void)animate { 
    [bg setFrame:CGRectMake(0, 0, 0, 0)]; 
    [UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{ 
     [bg setFrame:CGRectMake(0, 0, 1378, 1005)]; 
    } completion:nil]; 
} 

设置开始动画的状态(不要忘记删除通知观察员)

+2

_ + 1_通知是比使用应用程序委托更好地接收此类事件的方式。 – Tricertops 2014-06-12 14:09:40

+1

+1这绝对是用于重新启动循环动画的更灵活和优雅的解决方案。UIApplicationDelegate不必关心重新启动MYWaitCycleView的旋转鸡动画。 @ Ramiro的代码非常开心,坐在UIViewController或UIView子类中,靠近被动画的东西。 – 2014-09-22 22:13:45

+0

我已经使用了这种方法好几年了,但是现在遇到了动画仍然停止并且没有重新启动的问题(它们应该无限期地运行在我的案例中);它发生在点击警报时要求用户批准远程(和/或本地)推送警报。我还没有找到解决方案。顺便说一句我使用通知'UIApplicationDidBecomeActiveNotification'而不是'UIApplicationWillEnterForegroundNotification'。 – Jonny 2017-01-05 03:07:26

1

这是非常重要的方式会更好,只需注册ApplicationDelegate就会成为方法和观察者的状态。

- (void)pauseAnimate{ 
    CFTimeInterval pausedTime = [self.layer timeOffset]; 
    self.layer.speed = 1.0; 
    self.layer.timeOffset = 0.0; 
    self.layer.beginTime = 0.0; 
    CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; 
    self.layer.beginTime = timeSincePause; 
} 

- (void)stopAnimate{ 
    CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil]; 
    self.layer.speed = 0.0; 
    self.layer.timeOffset = pausedTime; 
} 
1

这里比每次从背景中重新开始整个动画更好的解决方案。

对于斯威夫特3,你可以继承这个类:

class ViewWithPersistentAnimations : UIView { 
    private var persistentAnimations: [String: CAAnimation] = [:] 
    private var persistentSpeed: Float = 0.0 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     self.commonInit() 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     self.commonInit() 
    } 

    func commonInit() { 
     NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil) 
     NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil) 
    } 

    deinit { 
     NotificationCenter.default.removeObserver(self) 
    } 

    func didBecomeActive() { 
     self.restoreAnimations(withKeys: Array(self.persistentAnimations.keys)) 
     self.persistentAnimations.removeAll() 
     if self.persistentSpeed == 1.0 { //if layer was plaiyng before backgorund, resume it 
      self.layer.resume() 
     } 
    } 

    func willResignActive() { 
     self.persistentSpeed = self.layer.speed 

     self.layer.speed = 1.0 //in case layer was paused from outside, set speed to 1.0 to get all animations 
     self.persistAnimations(withKeys: self.layer.animationKeys()) 
     self.layer.speed = self.persistentSpeed //restore original speed 

     self.layer.pause() 
    } 

    func persistAnimations(withKeys: [String]?) { 
     withKeys?.forEach({ (key) in 
      if let animation = self.layer.animation(forKey: key) { 
       self.persistentAnimations[key] = animation 
      } 
     }) 
    } 

    func restoreAnimations(withKeys: [String]?) { 
     withKeys?.forEach { key in 
      if let persistentAnimation = self.persistentAnimations[key] { 
       self.layer.add(persistentAnimation, forKey: key) 
      } 
     } 
    } 
} 

extension CALayer { 
    func pause() { 
     if self.isPaused() == false { 
      let pausedTime: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil) 
      self.speed = 0.0 
      self.timeOffset = pausedTime 
     } 
    } 

    func isPaused() -> Bool { 
     return self.speed == 0.0 
    } 

    func resume() { 
     let pausedTime: CFTimeInterval = self.timeOffset 
     self.speed = 1.0 
     self.timeOffset = 0.0 
     self.beginTime = 0.0 
     let timeSincePause: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil) - pausedTime 
     self.beginTime = timeSincePause 
    } 
} 

很会照顾的当前状态,并暂停所有动画重新添加它,当应用程序来自于背景 - 无需复位它们。

要点:https://gist.github.com/grzegorzkrukowski/a5ed8b38bec548f9620bb95665c06128