अंतर्निहित एनिमेशन को अक्षम करना - [CALayer setNeedsDisplayInRect:]


137

मुझे कुछ जटिल ड्राइंग कोड वाली परत वापस ले ली गई है। InteonCxttext: मेथड। मैं ड्राइंग की मात्रा को कम से कम करने की कोशिश कर रहा हूं जो मुझे करने की आवश्यकता है, इसलिए मैं उपयोग कर रहा हूं -NeedsDisplayInRect: बस बदले गए भागों को अपडेट करने के लिए। यह शानदार तरीके से काम कर रहा है। हालाँकि, जब ग्राफिक्स सिस्टम मेरी परत को अपडेट करता है, तो यह क्रॉस-फेड का उपयोग करके पुरानी से नई छवि में परिवर्तित हो जाता है। मैं इसे तुरंत स्विच करना चाहूंगा।

मैंने क्रियाओं को बंद करने और शून्य करने के लिए अवधि निर्धारित करने के लिए कैटरशिप का उपयोग करने की कोशिश की है, और न ही काम किया है। यहां वह कोड है जिसका मैं उपयोग कर रहा हूं:

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

क्या कैटरशिप पर एक अलग विधि है जिसका मुझे इसके बजाय उपयोग करना चाहिए (मैंने भी -Vetue: forKey: kCATransactionDisableActions, उसी परिणाम के साथ) का उपयोग किया।


आप इसे अगले रन लूप में कर सकते हैं: dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });
हाशम अबोनाजमी

1
मुझे काम करने के लिए नीचे कई उत्तर मिले। इसके अलावा मददगार है Apple का लेयर चेंजिंग डिफॉल्ट बिहेवियर डॉक्यूमेंट, जिसमें निहित कार्रवाई निर्णय प्रक्रिया के बारे में विस्तार से बताया गया है।
16:euroburɳ

यह इस पर एक डुप्लिकेट प्रश्न है: stackoverflow.com/a/54656717/5067402
रयान फ्रांसेस्कोनी

जवाबों:


172

आप [NSNull null]उचित कुंजी के लिए एक एनीमेशन के रूप में वापस जाने के लिए परत पर क्रिया शब्दकोश को सेट करके ऐसा कर सकते हैं । उदाहरण के लिए, मैं उपयोग करता हूं

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

layer.actions = newActions;

मेरी परतों में से एक के भीतर प्रविष्टि या सबलेयर के बदलाव पर / बाहर एनिमेशन को फीका करने के लिए, साथ ही साथ परत के आकार और सामग्री में परिवर्तन। मेरा मानना ​​है कि contentsकुंजी वह है जिसे आप अपडेट किए गए ड्राइंग पर क्रॉसफ़ेड को रोकने के लिए देख रहे हैं।


स्विफ्ट संस्करण:

let newActions = [
        "onOrderIn": NSNull(),
        "onOrderOut": NSNull(),
        "sublayers": NSNull(),
        "contents": NSNull(),
        "bounds": NSNull(),
    ]

24
फ्रेम को बदलते समय गति को रोकने के लिए @"position"कुंजी का उपयोग करें ।
mxcl

11
@"hidden"एक्शन डिक्शनरी में भी संपत्ति को जोड़ना सुनिश्चित करें, भले ही आप उस तरह की परत की दृश्यता को टॉगल कर रहे हों और अपारदर्शिता एनीमेशन को निष्क्रिय करना चाहते हों।
एंड्रयू

1
@ ब्रैडलर्सन का यही विचार है कि मैं कुछ संघर्ष (i overrode actionForKey:बजाय), खोज , और fontSize, के बाद आया । ऐसा लगता है कि आप विधि में उपयोग की जाने वाली किसी भी कुंजी को निर्दिष्ट कर सकते हैं , वास्तव में जैसे जटिल कुंजी पथ निर्दिष्ट कर रहे हैं । contentsonLayoutboundssetValue:forKey:bounds.size
pqnet

11
इन 'विशेष' स्ट्रिंग्स के लिए वास्तव में स्थिरांक हैं जो किसी संपत्ति का प्रतिनिधित्व नहीं करते हैं (जैसे @ onOrderOut "के लिए kCAOnOrderOut") यहां पर अच्छी तरह से प्रलेखित किया गया है: developer.apple.com/library/mac/#ddiction/Cocoa/Conceptual/…
पैट्रिक पिजनपेल

1
@Benjohn केवल कुंजी है कि एक इसी संपत्ति नहीं है स्थिरांक परिभाषित किया है। BTW, लिंक मृत प्रतीत हो रहा है, यहाँ नया URL है: developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…
पैट्रिक पिजनपेल

89

इसके अलावा:

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

//foo

[CATransaction commit];

3
आप मूल प्रश्न का उत्तर देने के //fooसाथ बदल सकते हैं [self setNeedsDisplayInRect: rect]; [self displayIfNeeded];
कारोई लोरेंटेई

1
धन्यवाद! यह मुझे अपने कस्टम दृश्य पर एक एनिमेटेड ध्वज सेट करने देता है। एक टेबल व्यू सेल के भीतर उपयोग के लिए आसान (जहां सेल का पुन: उपयोग स्क्रॉल करते समय कुछ trippy एनिमेशन हो सकता है)।
जो डी 'एंड्रिया

3
मेरे लिए प्रदर्शन के मुद्दों की ओर जाता है, कार्रवाई सेट करना अधिक प्रदर्शनकारी है
पास्कलियस

26
शॉर्टहैंड:[CATransaction setDisableActions:YES]
टाइटेनियम

7
@Titaniumdecoy टिप्पणी में जोड़ना, यदि किसी को भ्रम हो गया है (जैसे कि मैं), [CATransaction setDisableActions:YES]सिर्फ एक [CATransaction setValue:forKey:]पंक्ति के लिए आशुलिपि है । आपको अभी भी लाइनों beginऔर commitलाइनों की आवश्यकता है ।
हुलंग

31

जब आप एक परत की संपत्ति को बदलते हैं, तो सीए आमतौर पर परिवर्तन को चेतन करने के लिए एक अंतर्निहित लेनदेन वस्तु बनाता है। यदि आप परिवर्तन को चेतन नहीं करना चाहते हैं, तो आप स्पष्ट लेन-देन करके और इसकी kCATransactionDisableActions गुण को सही पर सेट करके अंतर्निहित एनिमेशन को अक्षम कर सकते हैं ।

उद्देश्य सी

[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()

6
setDisableActions: वही करता है।
बेन सिनक्लेयर

3
यह एक सबसे आसान तरीका था, जो मुझे स्विफ्ट में काम करने के लिए मिला!
जम्बमान

@Andy द्वारा टिप्पणी अब तक का सबसे अच्छा और आसान तरीका है!
Aᴄʜᴇʀᴏɴғᴀɪʟ

23

ब्रैड लार्सन के जवाब के अलावा : कस्टम परतों के लिए (जो आपके द्वारा बनाई गई हैं) आप परत के 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, क्योंकि वास्तविक रिडरिंग कभी-कभी हो सकती है। अच्छा दृष्टिकोण कस्टम गुणों का उपयोग करना है, जैसा कि इस उत्तर में वर्णित है


यह मेरे लिए काम नहीं कर रहा है। यहाँ देखें।
aleclarson

हममम। मेरे पास इस दृष्टिकोण के साथ कभी कोई समस्या नहीं थी। लिंक किए गए प्रश्न में कोड ठीक दिखता है और शायद समस्या किसी अन्य कोड के कारण होती है।
स्कोज़िन

आह, मैं देख रहा हूं कि आप पहले से ही हल कर चुके हैं कि यह गलत था CALayerजो noImplicitAnimationsकाम करने से रोकता था । हो सकता है कि आप अपने स्वयं के उत्तर को सही मानें और समझाएं कि उस परत में क्या गलत था?
स्कोज़िन

मैं बस गलत CALayerउदाहरण के साथ परीक्षण कर रहा था (मेरे पास उस समय दो थे)।
एलेक्लेरसन

1
अच्छा समाधान ... लेकिन प्रोटोकॉल को NSNullलागू नहीं करता CAActionहै और यह कोई प्रोटोकॉल नहीं है जिसमें केवल वैकल्पिक तरीके हैं। इस कोड के रूप में अच्छी तरह से दुर्घटना और आप भी अनुवाद नहीं कर सकते कि तेजी से। बेहतर समाधान: अपनी वस्तु को CAActionप्रोटोकॉल के अनुरूप बनाएं (एक खाली runActionForKey:object:arguments:विधि के साथ जो कुछ भी नहीं करता है) और के selfबजाय वापस लौटें [NSNull null]। समान प्रभाव लेकिन सुरक्षित (सुनिश्चित करने के लिए क्रैश नहीं होगा) और स्विफ्ट में भी काम करता है।
मेकी

9

यहाँ एक और अधिक कुशल समाधान है, स्वीकृत उत्तर के समान लेकिन स्विफ्ट के लिए । कुछ मामलों के लिए यह हर बार जब आप मूल्य को संशोधित करते हैं तो एक लेनदेन बनाने से बेहतर होगा जो एक प्रदर्शन चिंता का विषय है क्योंकि दूसरों ने उल्लेख किया है जैसे कि 60fps पर परत की स्थिति को घसीटने का सामान्य उपयोग-मामला।

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

परत क्रियाओं को कैसे हल किया जाता है, इसके लिए सेब के डॉक्स देखें । प्रतिनिधि को लागू करना कैस्केड में एक और स्तर को छोड़ देगा, लेकिन मेरे मामले में प्रतिनिधि यूएवीईवाई को सेट करने की आवश्यकता वाले प्रतिनिधि के बारे में चेतावनी के कारण बहुत गड़बड़ था ।

संपादित करें: टिप्पणी करने वाले को धन्यवाद देता है कि यह NSNullकिसके अनुरूप है CAAction


NullActionस्विफ्ट के लिए कोई निर्माण करने की आवश्यकता नहीं है , पहले से ही आप उसी के NSNullअनुरूप CAActionकर सकते हैं जो आप उद्देश्य सी में करते हैं: layer.actions = ["स्थिति": NSNull ()]
user5649358

मैं इस एक के साथ अपने जवाब संयुक्त मेरी animating CATextLayer ठीक करने के लिए stackoverflow.com/a/5144221/816017
एरिक Zivkovic

यह मेरी परियोजना में CALayer लाइनों का रंग बदलने पर "एनीमेशन" देरी को बायपास करने के लिए आवश्यक मेरी समस्या के लिए एक महान तय था। धन्यवाद!!
प्लेटरेवरब

छोटा एवं सुन्दर! महान समाधान!
डेविड एच

7

सैम के उत्तर के आधार पर, और साइमन की कठिनाइयों ... CSShapeLayer बनाने के बाद प्रतिनिधि संदर्भ जोड़ें:

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

... "एम" फ़ाइल में कहीं और ...

मूल रूप से सैम के समान ही कस्टम "अक्षमImplicitAnimations" चर व्यवस्था के माध्यम से टॉगल करने की क्षमता के बिना है। एक "हार्ड-वायर" दृष्टिकोण से अधिक।

- (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];

}

7

वास्तव में, मुझे कोई भी उत्तर सही नहीं लगा। मेरे लिए समस्या हल करने वाली विधि यह थी:

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

फिर आप इसमें जो भी तर्क दे सकते हैं, एक विशिष्ट एनीमेशन को निष्क्रिय करने के लिए, लेकिन जब से मैं उन सभी को निकालना चाहता था, मैं शून्य लौटा।


5

स्विफ्ट में अंतर्निहित परत एनिमेशन को अक्षम करने के लिए

CATransaction.setDisableActions(true)

इस उत्तर के लिए धन्यवाद। मैंने पहली बार उपयोग करने की कोशिश की, disableActions()क्योंकि ऐसा लगता है कि यह वही काम करता है, लेकिन यह वास्तव में वर्तमान मूल्य प्राप्त करना है। मुझे लगता है कि यह @discardableभी चिह्नित है, जिससे यह मुश्किल हो जाएगा। स्रोत: developer.apple.com/documentation/quartzcore/catransaction/…
Austin

5

कुंजी के लिए CATransactionआंतरिक कॉल के अंदर कार्रवाई को अक्षम करने के लिए एक सरल विधि मिली :setValue:forKey:kCATransactionDisableActions

[CATransaction setDisableActions:YES];

स्विफ्ट:

CATransaction.setDisableActions(true)

2

इसे अपने कस्टम वर्ग में जोड़ें जहां आप कार्यान्वयन-वापस ले रहे हैं () विधि। अपनी आवश्यकताओं के अनुरूप करने के लिए कोड में बदलाव करें, मेरे लिए 'अस्पष्टता' ने क्रॉस-फेड एनीमेशन को रोकने की चाल चली।

-(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

यदि आपको कभी भी बहुत जल्दी (लेकिन बड़े पैमाने पर हैकी) की आवश्यकता होती है, तो इसे ठीक करें (स्विफ्ट):

let layer = CALayer()

// set other properties
// ...

layer.speed = 999

3
कृपया इसे कभी भी बंद न करें
m1h4

@ m1h4 इसके लिए धन्यवाद - कृपया समझाएं कि यह एक बुरा विचार क्यों है
मार्टिन सीआर

3
क्योंकि अगर किसी को निहित एनिमेशन को बंद करने की आवश्यकता है, तो ऐसा करने के लिए एक तंत्र है (या तो अस्थायी रूप से अक्षम कार्यों के साथ सीए लेनदेन या परत पर खाली कार्यों को स्पष्ट रूप से सेट करना)। बस एनीमेशन गति को कुछ उम्मीद से उच्च गति तक सेट करना, जिससे यह प्रतीत होता है कि यह अनावश्यक प्रदर्शन ओवरहेड के लोड का कारण बनता है (जो कि मूल लेखक का उल्लेख उसके लिए प्रासंगिक है) और विभिन्न दौड़-स्थितियों के लिए संभावित (ड्राइंग अभी भी एक अलग बफर में किया जाता है) बाद के बिंदु पर प्रदर्शन में एनिमेटेड होना - सटीक होना, ऊपर आपके मामले के लिए, 0.25 / 999 सेकंड के बाद)।
m1h4

यह वास्तव में एक शर्म की बात है जो view.layer?.actions = [:]वास्तव में काम नहीं करता है। गति सेट करना बदसूरत है लेकिन काम करता है।
tcurdt

1

IOS नहीं MacOS में केवल एक निहित संपत्ति एनीमेशन को तेज और अक्षम करने के लिए अपडेट किया गया

// Disable the implicit animation for changes to position
override open class func defaultAction(forKey event: String) -> CAAction? {
    if event == #keyPath(position) {
        return NSNull()
    }
    return super.defaultAction(forKey: event)
}

एक और उदाहरण, इस मामले में दो अंतर्निहित एनिमेशन को समाप्त करना।

class RepairedGradientLayer: CAGradientLayer {

    // Totally ELIMINATE idiotic implicit animations, in this example when
    // we hide or move the gradient layer

    override open class func defaultAction(forKey event: String) -> CAAction? {
        if event == #keyPath(position) {
            return NSNull()
        }
        if event == #keyPath(isHidden) {
            return NSNull()
        }
        return super.defaultAction(forKey: event)
    }
}

0

IOS 7 के रूप में एक सुविधा विधि है जो सिर्फ यही करती है:

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

1
मुझे विश्वास नहीं है कि यह विधि CALayer एनिमेशन को ब्लॉक करती है ।
बेंजो

1
@Benjohn आह मुझे लगता है कि तुम सही हो। अगस्त में जितना पता नहीं था। क्या मुझे यह उत्तर हटा देना चाहिए?
वारलिंग

:-) मुझे या तो यकीन नहीं है, क्षमा करें! टिप्पणियां वैसे भी अनिश्चितता का संचार करती हैं, इसलिए यह शायद ठीक है।
बेनजॉन

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 को जितना चाहें अपडेट कर सकते हैं =)

इस से प्रेरित है , और यह जवाब।


0

इसे इस्तेमाल करे।

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

चेतावनी

यदि आप UITableView उदाहरण के प्रतिनिधि को सेट करते हैं, तो कभी-कभी दुर्घटना हो सकती है। (शायद स्क्रॉलव्यू के हिटस्ट को पुनरावर्ती कहा जाता है।)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.