एनिमेशनडायटॉप प्रतिनिधि के भीतर CAAnimation की पहचान कैसे करें?


102

मुझे एक समस्या थी जहाँ मेरे पास ओवरलैपिंग की एक श्रृंखला थी कैरमेशन / सीएएनिमेशन सीक्वेंस, जिनमें से सभी को कस्टम ऑपरेशन करने की आवश्यकता थी जब एनिमेशन बंद हो गए, लेकिन मुझे केवल एनीमेशनडाइपटॉप के लिए एक प्रतिनिधि हैंडलर चाहिए था।

हालाँकि, मुझे एक समस्या थी, एनीमेशनडायडटॉप प्रतिनिधि में प्रत्येक कैटरेन्स / CAAimimation को विशिष्ट रूप से पहचानने का एक तरीका प्रतीत नहीं हुआ।

मैंने CAAnimation के हिस्से के रूप में उजागर की गई कुंजी / मूल्य प्रणाली के माध्यम से इस समस्या को हल किया।

जब आप अपना एनीमेशन शुरू करते हैं, तो अपने पहचानकर्ता और मान सेट करने के लिए CATransition / CAAnimation पर setValue पद्धति का उपयोग करें जब एनीमेशनडायडटॉप फायर होता है:

-(void)volumeControlFadeToOrange
{   
    CATransition* volumeControlAnimation = [CATransition animation];
    [volumeControlAnimation setType:kCATransitionFade];
    [volumeControlAnimation setSubtype:kCATransitionFromTop];
    [volumeControlAnimation setDelegate:self];
    [volumeControlLevel setBackgroundImage:[UIImage imageNamed:@"SpecialVolume1.png"] forState:UIControlStateNormal];
    volumeControlLevel.enabled = true;
    [volumeControlAnimation setDuration:0.7];
    [volumeControlAnimation setValue:@"Special1" forKey:@"MyAnimationType"];
    [[volumeControlLevel layer] addAnimation:volumeControlAnimation forKey:nil];    
}

- (void)throbUp
{
    doThrobUp = true;

    CATransition *animation = [CATransition animation]; 
    [animation setType:kCATransitionFade];
    [animation setSubtype:kCATransitionFromTop];
    [animation setDelegate:self];
    [hearingAidHalo setBackgroundImage:[UIImage imageNamed:@"m13_grayglow.png"] forState:UIControlStateNormal];
    [animation setDuration:2.0];
    [animation setValue:@"Throb" forKey:@"MyAnimationType"];
    [[hearingAidHalo layer] addAnimation:animation forKey:nil];
}

आपके एनिमेशनडीडटॉप प्रतिनिधि में:

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag{

    NSString* value = [theAnimation valueForKey:@"MyAnimationType"];
    if ([value isEqualToString:@"Throb"])
    {
       //... Your code here ...
       return;
    }


    if ([value isEqualToString:@"Special1"])
    {
       //... Your code here ...
       return;
    }

    //Add any future keyed animation operations when the animations are stopped.
 }

इसका दूसरा पहलू यह है कि यह आपको अपने प्रतिनिधि वर्ग में संग्रहीत करने के बजाय मुख्य मूल्य युग्मन प्रणाली में स्थिति रखने की अनुमति देता है। कम कोड, बेहतर।

मुख्य मूल्य जोड़ी कोडिंग पर Apple संदर्भ की जाँच करना सुनिश्चित करें ।

क्या एनिमेशनडायटॉप प्रतिनिधि में CAAimimation / CATransition पहचान के लिए बेहतर तकनीकें हैं?

धन्यवाद, - बातरग


4
बैटगर, जब मैंने "iphone एनीमेशनडीडटॉप पहचान" के लिए गुगली की, तो पहली हिट आपकी पोस्ट थी, जिसमें एनीमेशन को पहचानने के लिए की-वैल्यू के उपयोग का सुझाव दिया गया था। बस मुझे जो चाहिए था, धन्यवाद। रूडी
रदीफा

1
ध्यान दें कि CAAnimationकी delegateहै ताकि आप इसे स्थापित करने के लिए की आवश्यकता हो सकती, मजबूत है nilचक्र को बनाए रखने से बचने के लिए!
इयूलियन ओनोफ्रेई

जवाबों:


92

बत्गर की तकनीक बहुत जटिल है। AddAnimation में forKey पैरामीटर का लाभ क्यों नहीं उठाया गया? यह इस उद्देश्य के लिए बनाया गया था। बस सेटवैल्यू को कॉल को बाहर निकालें और कुंजी स्ट्रिंग को AddAnimation कॉल पर ले जाएं। उदाहरण के लिए:

[[hearingAidHalo layer] addAnimation:animation forKey:@"Throb"];

फिर, अपने एनिमेशनडायटॉप कॉलबैक में, आप कुछ ऐसा कर सकते हैं:

if (theAnimation == [[hearingAidHalo layer] animationForKey:@"Throb"]) ...

मैं इस बात का उल्लेख करना चाहूंगा कि उपरोक्त नियमों का उपयोग करना! चेतावनी दी। यही है, एनिमेशनफॉरके: आपके सीएएनिमेशन ऑब्जेक्ट की रिटेन गिनती बढ़ाता है।
mmilo

1
@mmilo जो बहुत आश्चर्य की बात नहीं है, क्या यह है? एक एनीमेशन को एक लेयर में जोड़कर, लेयर एनीमेशन का मालिक है, इसलिए एनीमेशन का रिटेन काउंट निश्चित रूप से बढ़ा हुआ है।
गोरिल्लापैच

16
काम नहीं करता है - जब तक स्टॉप चयनकर्ता को नहीं बुलाया जाता है, तब तक एनीमेशन मौजूद नहीं है। आपको एक शून्य संदर्भ मिलता है।
एडम

4
यह forKey का दुरुपयोग है: पैरामीटर, और इसके लिए कोई आवश्यकता नहीं है। बत्गर जो कर रहा था वह बिल्कुल सही है - की-वैल्यू कोडिंग आपको किसी भी मनमाने डेटा को अपने एनीमेशन से जोड़ने की अनुमति देता है, जिससे आप इसे आसानी से पहचान सकते हैं।
मैट

7
एडम, नीचे jimt का जवाब देखें - आपको इसे सेट करना होगा anim.removedOnCompletion = NO;ताकि यह तब भी मौजूद हो जब -animationDidStop:finished:इसे बुलाया जाए।
यांग मेयर

46

मैं अभी-अभी CAAnimations के लिए कोड पूरा करने का एक और बेहतर तरीका लेकर आया हूं:

मैंने एक ब्लॉक के लिए एक टाइफाइड बनाया:

typedef void (^animationCompletionBlock)(void);

और एक कुंजी जिसे मैं एक एनीमेशन में ब्लॉक जोड़ने के लिए उपयोग करता हूं:

#define kAnimationCompletionBlock @"animationCompletionBlock"

फिर, अगर मुझे CAAnimation के खत्म होने के बाद एनीमेशन पूरा करने का कोड चलाना है, तो मैं खुद को एनीमेशन के प्रतिनिधि के रूप में सेट करता हूं, और setValue: forKey का उपयोग करके एनीमेशन में कोड का एक ब्लॉक जोड़ देता हूं:

animationCompletionBlock theBlock = ^void(void)
{
  //Code to execute after the animation completes goes here    
};
[theAnimation setValue: theBlock forKey: kAnimationCompletionBlock];

फिर, मैं एक एनीमेशनडिप्ट कार्यान्वित करता हूं: समाप्त: विधि, जो निर्दिष्ट कुंजी पर एक ब्लॉक के लिए जांच करता है और यदि यह पाया जाता है तो इसे निष्पादित करता है:

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
  animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock];
  if (theBlock)
    theBlock();
}

इस दृष्टिकोण की सुंदरता यह है कि आप उसी स्थान पर सफाई कोड लिख सकते हैं जहां आप एनीमेशन ऑब्जेक्ट बनाते हैं। बेहतर अभी भी, क्योंकि कोड एक ब्लॉक है, इसमें एन्कोडिंग क्षेत्र में स्थानीय चर तक पहुंच है जिसमें इसे परिभाषित किया गया है। आपको उपयोगकर्ताइन्फो शब्दकोश या ऐसे अन्य बकवास सेट अप करने के साथ गड़बड़ करने की आवश्यकता नहीं है, और कभी भी बढ़ते एनीमेशनडायडटॉप को लिखना नहीं है: समाप्त: विधि जो अधिक से अधिक जटिल हो जाती है क्योंकि आप विभिन्न प्रकार के एनिमेशन जोड़ते हैं।

सच कहा जाए, CAAimimation में पूर्ण रूप से निर्मित ब्लॉक संपत्ति होनी चाहिए, और सिस्टम निर्दिष्ट होने पर इसे स्वचालित रूप से कॉल करने के लिए समर्थन। हालाँकि, उपरोक्त कोड आपको अतिरिक्त कोड की केवल कुछ पंक्तियों के साथ ही कार्यक्षमता प्रदान करता है।


7
किसी ने इसके लिए CAAimimation पर एक श्रेणी भी रखी: github.com/xissburg/CAAnimationBlocks
Jay Peyer

यह सही प्रतीत नहीं होता है। काफी बार, मुझे एक EXEC_Err मिलता है, जिसे theBlock();लागू किया जाता है, और मुझे विश्वास है कि यह इस तथ्य के कारण है कि ब्लॉक का दायरा नष्ट हो गया था।
महबूबज़

मैं थोड़ी देर के लिए ब्लॉक का उपयोग कर रहा हूं, और यह Apple के भयानक "आधिकारिक" दृष्टिकोण से बेहतर है।
एडम

3
मुझे पूरा यकीन है कि आपको किसी संपत्ति के मूल्य के रूप में सेट करने से पहले उस ब्लॉक को [ब्लॉक कॉपी] की आवश्यकता होगी।
फियोना हॉपकिंस

1
नहीं, आपको ब्लॉक को कॉपी करने की आवश्यकता नहीं है।
डंकन सी

33

दूसरा दृष्टिकोण केवल तभी काम करेगा जब आप स्पष्ट रूप से अपने एनीमेशन को सेट करते हैं इसे चलाने से पहले पूरा नहीं किया जाना चाहिए:

CAAnimation *anim = ...
anim.removedOnCompletion = NO;

यदि आप ऐसा करने में विफल रहते हैं, तो आपके एनीमेशन को पूरा होने से पहले हटा दिया जाएगा, और कॉलबैक शब्दकोश में नहीं मिलेगा।


10
यह एक टिप्पणी होनी चाहिए, एक जवाब नहीं।
तक

2
मुझे आश्चर्य है कि अगर इसे हटाने के बाद स्पष्ट रूप से इसे हटाने के लिए आवश्यक है, तो इसे हटा दें?
'14

यह वास्तव में निर्भर करता है कि आप क्या करना चाहते हैं। यदि आवश्यक हो तो आप इसे हटा सकते हैं या छोड़ सकते हैं क्योंकि आप मिलकर कुछ और करना चाहते हैं।
सेबजैक ४२

31

अन्य सभी उत्तर बहुत जटिल हैं! आप एनीमेशन की पहचान के लिए सिर्फ अपनी कुंजी क्यों नहीं जोड़ते?

यह समाधान बहुत ही आसान है जिसकी आपको आवश्यकता है कि आप अपनी स्वयं की कुंजी को एनीमेशन में जोड़ें (इस उदाहरण में एनीमेशनआईडी)

एनीमेशन 1 की पहचान करने के लिए इस लाइन को डालें :

[myAnimation1 setValue:@"animation1" forKey:@"animationID"];

और यह एनीमेशन 2 की पहचान करने के लिए :

[myAnimation2 setValue:@"animation2" forKey:@"animationID"];

इसे इस तरह से परखें:

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
{
    if([[animation valueForKey:@"animationID"] isEqual:@"animation1"]) {
    //animation is animation1

    } else if([[animation valueForKey:@"animationID"] isEqual:@"animation2"]) {
    //animation is animation2

    } else {
    //something else
    }
}

यह किसी भी उदाहरण चर की आवश्यकता नहीं है :


मुझे [animation valueForKey:@"animationID"]
एनीमेशनडीडटॉप

14

यह स्पष्ट करने के लिए कि ऊपर से क्या निहित है (और कुछ घंटों के बाद मुझे यहाँ लाया गया है): मूल एनीमेशन ऑब्जेक्ट को देखने की उम्मीद न करें जिसे आपने आवंटित किया था।

 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)flag 

जब एनीमेशन समाप्त हो जाता है, क्योंकि [CALayer addAnimation:forKey:]आपके एनीमेशन की एक प्रति बनाता है।

आप जिस पर भरोसा कर सकते हैं, वह यह है कि आपने अपने एनीमेशन ऑब्जेक्ट को दिए गए प्रमुख मान अभी भी animationDidStop:finished:संदेश के साथ पारित प्रतिकृति एनीमेशन ऑब्जेक्ट में बराबर मूल्य (लेकिन जरूरी नहीं कि सूचक समतुल्य) के साथ हैं । जैसा कि ऊपर उल्लेख किया गया है, केवीसी का उपयोग करें और आप राज्य को संग्रहीत और पुनर्प्राप्त करने के लिए पर्याप्त गुंजाइश प्राप्त करते हैं।


1
+1 यह सबसे अच्छा उपाय है! आप एनीमेशन का 'नाम' [animation setValue:@"myanim" forKey:@"name"]सेट कर सकते हैं और आप परत का उपयोग करके एनिमेटेड भी सेट कर सकते हैं [animation setValue:layer forKey:@"layer"]। ये मान तब प्रतिनिधि विधियों के भीतर पुनर्प्राप्त किए जा सकते हैं।
ट्रोजनफो

valueForKey:nilमेरे लिए रिटर्न , किसी भी विचार क्यों?
इयूलियन ओनोफ्रेई

@IulianOnofrei जाँच करें कि आपका एनीमेशन एक ही संपत्ति के लिए एक और एनीमेशन द्वारा विस्थापित नहीं किया गया था - अप्रत्याशित दुष्प्रभाव के रूप में हो सकता है।
t'rst

@ t0rst, क्षमा करें, कई एनिमेशन हैं और कॉपी पेस्ट का उपयोग करके, मैं एक ही एनीमेशन चर पर विभिन्न मान सेट कर रहा था।
इलियान ओनोफ्रेई

2

मैं ज्यादातर objc उत्तर देख सकता हूँ मैं ऊपर दिए गए सबसे अच्छे उत्तर के आधार पर स्विफ्ट 2.3 के लिए एक बनाऊंगा।

एक शुरुआत के लिए यह एक निजी संरचना पर उन सभी कुंजियों को संग्रहीत करने के लिए अच्छा होगा, इसलिए यह सुरक्षित है और भविष्य में इसे बदलना आपको परेशान करने वाले कीड़े नहीं लाएगा क्योंकि आप इसे कोड में हर जगह बदलना भूल गए:

private struct AnimationKeys {
    static let animationType = "animationType"
    static let volumeControl = "volumeControl"
    static let throbUp = "throbUp"
}

जैसा कि आप देख सकते हैं कि मैंने चर / एनिमेशन के नाम बदल दिए हैं इसलिए यह अधिक स्पष्ट है। अब एनीमेशन बनाते समय इन कीज़ को सेट करें।

volumeControlAnimation.setValue(AnimationKeys.volumeControl, forKey: AnimationKeys.animationType)

(...)

throbUpAnimation.setValue(AnimationKeys.throbUp, forKey: AnimationKeys.animationType)

फिर आखिर में जब एनीमेशन बंद हो जाता है तो प्रतिनिधि को संभालना

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
    if let value = anim.valueForKey(AnimationKeys.animationType) as? String {
        if value == AnimationKeys.volumeControl {
            //Do volumeControl handling
        } else if value == AnimationKeys.throbUp {
            //Do throbUp handling
        }
    }
}

0

Apple के कुंजी-मूल्य का उपयोग करने वाला IMHO ऐसा करने का सुरुचिपूर्ण तरीका है: यह विशेष रूप से वस्तुओं में एप्लिकेशन विशिष्ट डेटा जोड़ने की अनुमति देने के लिए है।

अन्य बहुत कम सुरुचिपूर्ण संभावना आपके एनीमेशन ऑब्जेक्ट्स के संदर्भों को संग्रहीत करने और उन्हें पहचानने के लिए एक सूचक तुलना करना है।


यह कभी काम नहीं करेगा - आप सूचक तुल्यता नहीं कर सकते, क्योंकि Apple पॉइंटर को बदलता है।
एडम

0

मेरे लिए यह जांचने के लिए कि क्या 2 कैबासिकैमिनेशन ऑब्जेक्ट एक ही एनीमेशन हैं, मैं बिल्कुल उसी तरह करने के लिए की -पैथ फ़ंक्शन का उपयोग करता हूं।

अगर ([एनीमेशनए की -पैथ] == [एनिमेशनबी की -पैथ])

  • CABasicAnimation के लिए KeyPath सेट करने की कोई आवश्यकता नहीं है क्योंकि यह अब चेतन नहीं होगा

सवाल कॉलबैक को सौंपने से संबंधित है, और कीपैथ CAAnimation पर एक विधि नहीं है
मैक्स मैकलेओड

0

मैं उपयोग करना पसंद करता हूं setValue:forKey: जिस दृश्य को मैं एनिमेट कर रहा हूं, उसका एक संदर्भ रखने के लिए, यह आईडी पर आधारित एनीमेशन को विशिष्ट रूप से पहचानने की कोशिश करने से अधिक सुरक्षित है क्योंकि उसी तरह के एनीमेशन को विभिन्न परतों में जोड़ा जा सकता है।

ये दोनों बराबर हैं:

[UIView animateWithDuration: 0.35
                 animations: ^{
                     myLabel.alpha = 0;
                 } completion: ^(BOOL finished) {
                     [myLabel removeFromSuperview];
                 }];

इसके साथ:

CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOut.fromValue = @([myLabel.layer opacity]);
fadeOut.toValue = @(0.0);
fadeOut.duration = 0.35;
fadeOut.fillMode = kCAFillModeForwards;
[fadeOut setValue:myLabel forKey:@"item"]; // Keep a reference to myLabel
fadeOut.delegate = self;
[myLabel.layer addAnimation:fadeOut forKey:@"fadeOut"];
myLabel.layer.opacity = 0;

और प्रतिनिधि विधि में:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    id item = [anim valueForKey:@"item"];

    if ([item isKindOfClass:[UIView class]])
    {
        // Here you can identify the view by tag, class type 
        // or simply compare it with a member object

        [(UIView *)item removeFromSuperview];
    }
}

0

Xcode 9 स्विफ्ट 4.0

आप एनीमेशन में लौटे एनीमेशन से जोड़े गए एनीमेशन से संबंधित कुंजी मूल्यों का उपयोग कर सकते हैं।

सभी सक्रिय एनिमेशन और संबंधित पूर्णताओं को शामिल करने के लिए एक शब्दकोश घोषित करें:

 var animationId: Int = 1
 var animating: [Int : () -> Void] = [:]

जब आप अपना एनीमेशन जोड़ते हैं, तो इसके लिए एक कुंजी सेट करें:

moveAndResizeAnimation.setValue(animationId, forKey: "CompletionId")
animating[animationId] = {
    print("completion of moveAndResize animation")
}
animationId += 1    

एनिमेशनडीडटॉप में, जादू होता है:

    let animObject = anim as NSObject
    if let keyValue = animObject.value(forKey: "CompletionId") as? Int {
        if let completion = animating.removeValue(forKey: keyValue) {
            completion()
        }
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.