2012-06-01 43 views
2

我想做一個對設備動作有反應的動畫,並且即使UI線程一時很忙,我也希望它保持平穩。動畫包括更改CALayer的貝塞爾路徑。我試過從輔助線程做到這一點,但偶爾會掛起主線程有堆棧的地方。我在做什麼完全沒有希望?下面是我在線程做:從後臺線程更改CALayer的屬性是否安全?

[CATransaction lock]; 
[CATransaction setValue:(id)kCFBooleanTrue 
       forKey:kCATransactionDisableActions]; 
[CATransaction begin]; 
myLayer.path = [UIBezierPath bezierPathWithOvalInRect:theRect].CGPath; 
myLayer.bounds = theBounds; 
[CATransaction commit]; 

[CATransaction flush]; 
[CATransaction setValue:(id)kCFBooleanFalse 
       forKey:kCATransactionDisableActions]; 
[CATransaction unlock]; 
+0

雖然你應該可以更新來自輔助線程的圖層,但我注意到了「核心動畫編程指南」中的相關細節:「形狀圖層...確實涉及渲染主線程上的形狀並緩存結果。不解釋崩潰,但它可能解釋掛起。 –

回答

2

更新從除uithread任何線程的UI被禁止 所以你必須做到以下幾點:

- (void) updateUI 
{ 
[CATransaction lock]; 
[CATransaction setValue:(id)kCFBooleanTrue 
       forKey:kCATransactionDisableActions]; 
[CATransaction begin]; 
myLayer.path = [UIBezierPath bezierPathWithOvalInRect:theRect].CGPath; 
myLayer.bounds = theBounds; 
[CATransaction commit]; 

[CATransaction flush]; 
[CATransaction setValue:(id)kCFBooleanFalse 
       forKey:kCATransactionDisableActions]; 
[CATransaction unlock]; 
} 

,並從其他線程

[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:YES]; 
+0

請注意,如果它的工作不要忘記接受:) –

+0

核心動畫鎖真的有必要嗎?另外,爲什麼要使用performSelectorOnMainThread而不是使用主調度隊列? –

+0

鎖是從原來的代碼,但是我猜他們可能是不必要的,關於performSelectorOnMainThread我添加它爲簡單,請隨時編輯:) –

4

這是我的版本。

dispatch_async(dispatch_get_main_queue(), ^{ 
      myLayer.path = [UIBezierPath bezierPathWithOvalInRect:theRect].CGPath; 
      myLayer.bounds = theBounds; 
     }); 

調度隊列給你能夠從局部範圍變量來拉動,而不必擔心執行的中間數據結構的優勢。

+0

您的調度隊列正在主隊列上運行,因此它不在後臺線程中,因此與問題無關。儘管如此,如果你把它放在背景排隊上,這還算不錯。 –

+2

問題是關於從後臺線程更改CALayer的屬性。這是不可能的,但下一個最好的實現是在主線程上排列一個更改。所以從這個意義上說,我認爲包含這段代碼片段會很有幫助,因爲這個問題的前提假設是不可能的。 –

+1

啊由於某種原因,我認爲接受的答案是調用performSelectorInBackground。沒關係,這比使用performSelector調用更好,更乾淨。 –