2011-05-18 54 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