2010-02-11 75 views
119

我已經有了一個在其-drawInContext:方法中具有一些複雜繪圖代碼的圖層。我想盡量減少我需要做的繪圖數量,所以我使用-setNeedsDisplayInRect:只更新更改的部分。這非常出色。但是,當圖形系統更新我的圖層時,它將使用交叉漸變從舊圖像轉換爲新圖像。我想立即切換。禁用隱式動畫 - [CALayer setNeedsDisplayInRect:]

我試過使用CATransaction來關閉操作並將持續時間設置爲零,但都不起作用。下面是我使用的代碼:有

[CATransaction begin]; 
[CATransaction setDisableActions: YES]; 
[self setNeedsDisplayInRect: rect]; 
[CATransaction commit]; 

是在CATransaction我應該使用不同的方法(我也試過-setValue:forKey:與kCATransactionDisableActions,相同的結果)。

+0

你可以在下一次運行循環中完成它:'dispatch_after(dispatch_time(DISPATCH_TI ME_NOW,(int64_t)(delay * NSEC_PER_SEC)),dispatch_get_main_queue(),^ { });' – 2015-06-17 14:04:20

+1

我在下面找到了很多答案爲我工作。此外,Apple還提供了[更改圖層的默認行爲](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/ReactingtoLayerChanges/ReactingtoLayerChanges.html)文檔,該文檔描述了隱式行爲決策過程詳情。 – 2015-10-26 16:41:44

回答

152

您可以通過設置圖層上的動作字典將[NSNull null]作爲相應鍵的動畫進行設置。例如,我使用

NSDictionary *newActions = @{ 
    @"onOrderIn": [NSNull null], 
    @"onOrderOut": [NSNull null], 
    @"sublayers": [NSNull null], 
    @"contents": [NSNull null], 
    @"bounds": [NSNull null] 
}; 

layer.actions = newActions; 

禁用/縮小插入或我的層中的一個內子層中的變化的動畫,以及在尺寸的變化和該層的內容褪色。我相信contents關鍵是您正在尋找的關鍵,以防止更新圖形上的交叉淡入淡出。

+1

布拉德,完美,謝謝! – 2010-02-11 14:30:45

+23

爲了防止更改框架時的移動,請使用「@」位置「」鍵。 – mxcl 2011-03-30 16:11:58

+11

如果以這種方式切換圖層的可見性並希望禁用不透明度動畫,請務必在操作字典中添加「@」隱藏的「'屬性」。 – Andrew 2011-12-29 22:58:25

87

另外:

[CATransaction begin]; 
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; 

//foo 

[CATransaction commit]; 
+2

你可以用'[self setNeedsDisplayInRect:rect]替換'// foo'; [self displayIfNeeded];'回答原來的問題。 – 2011-06-23 00:56:07

+1

謝謝!這可以讓我在自定義視圖上設置一個動畫標誌。方便在表格視圖單元格內使用(在滾動時,單元格重用可能會導致一些難以理解的動畫)。 – 2011-09-12 13:51:17

+3

導致我的性能問題,設置動作更具性能 – Pascalius 2012-03-08 10:44:53

23

除了Brad Larson's answer:定製層(由創建)you can use delegation而不是修改層的actions字典。這種方法更具動態性,可能更具性能。它允許禁用所有隱式動畫,而不必列出所有動畫鍵。

不幸的是,不可能使用UIView作爲自定義圖層委託,因爲每個UIView已經是它自己的圖層的委託。但是你可以使用一個簡單的輔助類是這樣的:

@interface MyLayerDelegate : NSObject 
    @property (nonatomic, assign) BOOL disableImplicitAnimations; 
@end 

@implementation MyLayerDelegate 

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event 
{ 
    if (self.disableImplicitAnimations) 
     return (id)[NSNull null]; // disable all implicit animations 
    else return nil; // allow implicit animations 

    // you can also test specific key names; for example, to disable bounds animation: 
    // if ([event isEqualToString:@"bounds"]) return (id)[NSNull null]; 
} 

@end 

用法(視圖內):

MyLayerDelegate *delegate = [[MyLayerDelegate alloc] init]; 

// assign to a strong property, because CALayer's "delegate" property is weak 
self.myLayerDelegate = delegate; 

self.myLayer = [CALayer layer]; 
self.myLayer.delegate = delegate; 

// ... 

self.myLayerDelegate.disableImplicitAnimations = YES; 
self.myLayer.position = (CGPoint){.x = 10, .y = 42}; // will not animate 

// ... 

self.myLayerDelegate.disableImplicitAnimations = NO; 
self.myLayer.position = (CGPoint){.x = 0, .y = 0}; // will animate 

有時是方便的視圖的作爲視圖的自定義子層的委託控制器;在這種情況下,不需要輔助類,您可以在控制器內部實現actionForLayer:forKey:方法。

重要提示:不要試圖修改UIView的底層的代表(例如,使隱式動畫) - 不好的事情會發生:)

注意:如果要動畫(未禁用動畫對於)圖層重繪,將[CALayer setNeedsDisplayInRect:]調用放入CATransaction是無用的,因爲實際重繪可能(並且可能會)稍後發生。好的方法是使用自定義屬性,如in this answer所述。

+0

這不適合我。 [見這裏。](http://stackoverflow.com/questions/25830050/disabling-calayer-implicit-animations) – aleclarson 2014-09-14 04:30:04

+0

嗯。我從來沒有遇到過這種方法的問題。鏈接問題中的代碼看起來不錯,可能問題是由其他代碼引起的。 – skozin 2014-11-14 01:28:27

+0

嗯,我看到你已經解決了錯誤的'CALayer',它阻止了'noImplicitAnimations'的工作。也許你應該將自己的答案標記爲正確,並解釋該層出了什麼問題? – skozin 2014-11-14 01:31:37

6

其實,我沒有找到任何答案是正確的。能解決問題,我的方法是這樣的:

- (id<CAAction>)actionForKey:(NSString *)event { 
    return nil; 
} 

然後你就可以在任何邏輯是,禁止特定的動畫,但因爲我想刪除他們,我回到零。

6

基於薩姆的回答,和西蒙的困難......創建CSShapeLayer後添加代表參考:

CAShapeLayer *myLayer = [CAShapeLayer layer]; 
myLayer.delegate = self; // <- set delegate here, it's magic. 

...其他地方的「M」的文件...

本質上與Sam's相同,但無法通過自定義「disableImplicitAnimations」變量排列進行切換。更多的是「硬線」方法。

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event { 

    // disable all implicit animations 
    return (id)[NSNull null]; 

    // allow implicit animations 
    // return nil; 

    // you can also test specific key names; for example, to disable bounds animation: 
    // if ([event isEqualToString:@"bounds"]) return (id)[NSNull null]; 

} 
+0

這不適用於我。 [見這裏。](http://stackoverflow.com/questions/25830050/disabling-calayer-implicit-animations) – aleclarson 2014-09-14 04:30:56

24

當您更改層的屬性,CA通常會創建一個隱含的交易對象進行動畫的變化。如果您不想動畫更改,則可以通過創建明確的事務並將其設置爲kCATransactionDisableActions屬性爲true來禁用隱式動畫。

Objective-C的

[CATransaction begin]; 
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; 
// change properties here without animation 
[CATransaction commit]; 

斯威夫特

CATransaction.begin() 
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) 
// change properties here without animation 
CATransaction.commit() 
+4

setDisableActions:也一樣。 – Andy 2014-08-23 13:29:11

+3

這是我在Swift工作的最簡單的解決方案! – Jambaman 2015-12-13 14:10:38

2

添加到您的自定義類,你要實現-drawRect()方法。對代碼進行更改以滿足您的需求,因爲我的「不透明」技巧阻止了淡入淡出動畫。

-(id<CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)key 
{ 
    NSLog(@"key: %@", key); 
    if([key isEqualToString:@"opacity"]) 
    { 
     return (id<CAAction>)[NSNull null]; 
    } 

    return [super actionForLayer:layer forKey:key]; 
} 
1

找到了一個更簡單的方法來禁用一個CATransaction在內部調用setValue:forKey:kCATransactionDisableActions鍵內的行動:

[CATransaction setDisableActions:YES]; 
0

由於的iOS 7有一個方便的方法,不只是這一點:

[UIView performWithoutAnimation:^{ 
    // apply changes 
}]; 
+1

我不認爲這種方法會阻止[CALayer動畫](https://www.objc.io/issues/5-ios7/iOS7-hidden-gems-and-workarounds/#blocking-animations)。 – Benjohn 2016-02-19 18:46:52

+1

@Benjohn我認爲你是對的。八月份的時候並不多。我應該刪除這個答案嗎? – Warpling 2016-02-20 19:22:01

+0

:-)我也不確定,對不起!無論如何,評論都會傳達不確定性,所以它可能沒問題。 – Benjohn 2016-02-22 11:02:16

6

下面是一個更有效的解決方案,類似於接受的答案但對於Swift。在某些情況下,每次修改其他人提到的性能問題值時,創建交易都會更好。拖動圖層位置在60fps左右的常見用例。

// Disable implicit position animation. 
layer.actions = ["position": NSNull()]  

查看apple的文檔how layer actions are resolved。實現委託會跳過級聯中的一個級別,但在我的情況下,由於caveat about the delegate needing to be set to the associated UIView而太亂了。

編輯:更新感謝評論者指出,NSNull符合CAAction

+0

我結合你的答案和這一個修復我的動畫CATextLayer http://stackoverflow.com/a/5144221/816017 – 2016-03-11 20:18:49

+0

沒有必要爲Swift創建一個** NullAction ** ** ** NSNull **符合以**'CAAction' **已經所以你可以做同樣的目標在C:layer.actions = [「position」:NSNull()] – user5649358 2015-12-07 09:45:57

+0

這是一個很好的解決我的問題需要繞過「動畫「在我的項目中更改CALayer行的顏色時會延遲。謝謝!! – PlateReverb 2017-04-02 17:12:44

0

要改變CATextLayer的字符串屬性時禁用煩人(模糊)的動畫,你可以這樣做:

class CANullAction: CAAction { 
    private static let CA_ANIMATION_CONTENTS = "contents" 

    @objc 
    func runActionForKey(event: String, object anObject: AnyObject, arguments dict: [NSObject : AnyObject]?) { 
     // Do nothing. 
    } 
} 

,然後用它像這樣(不要忘了正確設置你的CATextLayer ,例如正確的字體等):

caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()] 

你可以看到我的CATextLayer的完整安裝位置:

private let systemFont16 = UIFont.systemFontOfSize(16.0) 

caTextLayer = CATextLayer() 
caTextLayer.foregroundColor = UIColor.blackColor().CGColor 
caTextLayer.font = CGFontCreateWithFontName(systemFont16.fontName) 
caTextLayer.fontSize = systemFont16.pointSize 
caTextLayer.alignmentMode = kCAAlignmentCenter 
caTextLayer.drawsAsynchronously = false 
caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()] 
caTextLayer.contentsScale = UIScreen.mainScreen().scale 
caTextLayer.frame = CGRectMake(playbackTimeImage.layer.bounds.origin.x, ((playbackTimeImage.layer.bounds.height - playbackTimeLayer.fontSize)/2), playbackTimeImage.layer.bounds.width, playbackTimeLayer.fontSize * 1.2) 

uiImageTarget.layer.addSublayer(caTextLayer) 
caTextLayer.string = "The text you want to display" 

現在,只要你想你=可以更新caTextLayer.string多)

通過this啓發,並this回答。

0

試試這個。

let layer = CALayer() 
layer.delegate = hoo // Same lifecycle UIView instance. 

警告

如果設置的UITableView實例的代表,有時會發生崩潰。(可能遞歸調用滾動型的則hitTest。)

0

如果你需要一個非常快速的(但不可否認哈克)修復它可能是值得幹的(Swift):

let layer = CALayer() 

// set other properties 
// ... 

layer.speed = 999 
+1

請不要這樣做ffs – m1h4 2017-09-09 09:19:26

+0

@ m1h4感謝那 - 請解釋爲什麼這是一個壞主意 – 2017-09-10 10:56:26

+1

因爲如果需要關閉隱式動畫,有一種機制可以做到這一點(無論是臨時禁用操作的ca事務還是明確設置空白的動作到一個圖層上)。只需將動畫速度設置爲足夠高以使其看起來即時就會導致不必要的性能開銷(原始作者提及的與他相關)的負載以及各種競賽條件的可能性(繪圖仍被分成單獨的緩衝區以完成在稍後的時間點進行顯示 - 準確地說,對於上述情況,在0.25/999秒後)。 – m1h4 2017-09-11 11:45:22