2012-12-15 40 views
12

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

-(IBAction)moveDown:(id)sender{ 

    CGRect position = [[container.layer presentationLayer] frame]; 

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)]; 

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 310)]; 

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
    moveAnim.path = movePath.CGPath; 
    moveAnim.removedOnCompletion = NO; 
    moveAnim.fillMode = kCAFillModeForwards; 


    CAAnimationGroup *animGroup = [CAAnimationGroup animation]; 
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil]; 
    animGroup.duration = 2.0; 
    animGroup.removedOnCompletion = NO; 
    animGroup.fillMode = kCAFillModeForwards; 

    [container.layer addAnimation:animGroup forKey:nil]; 
} 
-(IBAction)moveUp:(id)sender{ 
    CGRect position = [[container.layer presentationLayer] frame]; 

    UIBezierPath *movePath = [UIBezierPath bezierPath]; 

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)]; 

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 115)]; 

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
    moveAnim.path = movePath.CGPath; 
    moveAnim.removedOnCompletion = NO; 
    moveAnim.fillMode = kCAFillModeForwards; 

    CAAnimationGroup *animGroup = [CAAnimationGroup animation]; 
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil]; 
    animGroup.duration = 2.0; 
    animGroup.removedOnCompletion = NO; 
    animGroup.fillMode = kCAFillModeForwards; 

    [container.layer addAnimation:animGroup forKey:nil]; 

} 

लेकिन लाइन

CGRect position = [[container.layer presentationLayer] frame]; 

केवल गंतव्य नहीं स्थिति वर्तमान स्थिति लौटा रहा है: यहाँ मेरी कोड है। मुझे मूल रूप से मुझे कंटेनर की वर्तमान स्थिति देने की ज़रूरत है जो एक बार जब मैं बटन छोड़ देता हूं, तो मैं अगली एनीमेशन कर सकता हूं। मेरे पास अब क्या काम नहीं करता है।

उत्तर

43

मैंने 100% सुनिश्चित करने के लिए पर्याप्त कोड का विश्लेषण नहीं किया है क्यों [[container.layer presentationLayer] frame] आपकी अपेक्षाओं को वापस नहीं कर सकता है। लेकिन मुझे कई समस्याएं दिखाई देती हैं।

एक स्पष्ट समस्या यह है कि moveDown:movePath घोषित नहीं करता है। यदि movePath एक आवृत्ति चर है, तो संभवतः आप इसे साफ़ करना चाहते हैं या हर बार moveDown: कहलाते हुए एक नया उदाहरण बनाना चाहते हैं, लेकिन मुझे ऐसा नहीं लगता है।

एक कम स्पष्ट समस्या यह है कि (presentationLayer के आपके उपयोग के बावजूद removedOnCompletion और fillMode, के आपके उपयोग से पहचानने) आप जाहिरा तौर पर समझ में नहीं आता कि कैसे कोर एनिमेशन काम करता है। यह आश्चर्यजनक रूप से आम है, इसलिए अगर मैं गलत हूं तो मुझे माफ़ कर दो। वैसे भी, पढ़ें, क्योंकि मैं समझाऊंगा कि कोर एनीमेशन कैसे काम करता है और फिर आपकी समस्या को कैसे ठीक किया जाए।

कोर एनीमेशन में, जिस परत वस्तु का आप सामान्य रूप से काम करते हैं वह मॉडल परत है। जब आप किसी परत पर एनीमेशन संलग्न करते हैं, तो कोर एनीमेशन मॉडल परत की एक प्रति बनाता है, जिसे प्रस्तुति परत कहा जाता है, और एनीमेशन प्रस्तुति परत के गुणों को समय के साथ बदलता है। एक एनीमेशन मॉडल परत के गुणों को कभी नहीं बदलता है।

जब एनीमेशन समाप्त होता है, और (डिफ़ॉल्ट रूप से) हटा दिया जाता है, प्रस्तुति परत नष्ट हो जाती है और मॉडल परत की गुणों के मान फिर से प्रभावी होते हैं। तो स्क्रीन पर परत अपनी मूल स्थिति/रंग/जो भी हो, "वापस स्नैप" लगती है।

एक आम है, लेकिन गलत तरह से इसे ठीक करने के एनीमेशन के removedOnCompletionNO और उसके fillModekCAFillModeForwards करने के लिए सेट किया जा सके। जब आप ऐसा करते हैं, तो प्रस्तुति परत चारों ओर लटकती है, इसलिए स्क्रीन पर "स्नैप बैक" नहीं है। समस्या यह है कि अब आपके पास मॉडल परत की तुलना में अलग-अलग मानों के साथ प्रस्तुति परत लटक रही है। यदि आप एनिमेटेड प्रॉपर्टी के मूल्य के लिए मॉडल लेयर (या दृश्य का मालिक है) पूछते हैं, तो आपको एक वैल्यू मिलेगा जो स्क्रीन पर क्या है उससे अलग है। और यदि आप फिर से संपत्ति को एनिमेट करने का प्रयास करते हैं, तो एनीमेशन शायद गलत जगह से शुरू होगा।

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

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

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y { 
    CGPoint fromValue = [layer.presentationLayer position]; 
    CGPoint toValue = CGPointMake(fromValue.x, y); 

    layer.position = toValue; 

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    animation.fromValue = [NSValue valueWithCGPoint:fromValue]; 
    animation.toValue = [NSValue valueWithCGPoint:toValue]; 
    animation.duration = 2; 
    [layer addAnimation:animation forKey:animation.keyPath]; 
} 

सूचना है कि हम एनीमेशन एक महत्वपूर्ण दे रहे हैं जब मैं यह परत में जोड़ें। चूंकि हम हर बार एक ही कुंजी का उपयोग करते हैं, इसलिए प्रत्येक एनीमेशन पूर्व एनीमेशन को अभी तक समाप्त नहीं होने पर पूर्व एनीमेशन को प्रतिस्थापित करेगा (हटाएं)।

बेशक

, जैसे ही आप इस के साथ खेलते हैं, तो आप पाएंगे कि यदि आप moveUp: जब moveDown: केवल आधा समाप्त हो गया है, moveUp: एनीमेशन आधा गति से होने के लिए यह अभी भी 2 सेकंड की अवधि है क्योंकि दिखाई देगा लेकिन यात्रा करने के लिए केवल आधा। हम वास्तव में दूरी के आधार पर अवधि की गणना करना चाहिए कूच किया जाना है:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY { 
    CGPoint fromValue = [layer.presentationLayer position]; 
    CGPoint toValue = CGPointMake(fromValue.x, y); 

    layer.position = toValue; 

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    animation.fromValue = [NSValue valueWithCGPoint:fromValue]; 
    animation.toValue = [NSValue valueWithCGPoint:toValue]; 
    animation.duration = 2.0 * (toValue.y - fromValue.y)/(y - baseY); 
    [layer addAnimation:animation forKey:animation.keyPath]; 
} 

आप वास्तव में यह की जरूरत है एक एनीमेशन समूह में एक मुख्यपथ एनीमेशन होने के लिए, अपने प्रश्न हमें क्यों आप उन चीजों की जरूरत है दिखाना चाहिए । वैसे भी, यह भी उन चीजों के साथ काम करता है:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY { 
    CGPoint fromValue = [layer.presentationLayer position]; 
    CGPoint toValue = CGPointMake(fromValue.x, y); 

    layer.position = toValue; 

    UIBezierPath *path = [UIBezierPath bezierPath]; 
    [path moveToPoint:fromValue]; 
    [path addLineToPoint:toValue]; 

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
    animation.path = path.CGPath; 
    animation.duration = 2.0 * (toValue.y - fromValue.y)/(y - baseY); 

    CAAnimationGroup *group = [CAAnimationGroup animation]; 
    group.duration = animation.duration; 
    group.animations = @[animation]; 
    [layer addAnimation:group forKey:animation.keyPath]; 
} 

तुम मेरे परीक्षण कार्यक्रम in this gist के लिए पूर्ण कोड पा सकते हैं। बस एक नया सिंगल व्यू एप्लिकेशन प्रोजेक्ट बनाएं और जिस्ट की सामग्री के साथ ViewController.m की सामग्री को प्रतिस्थापित करें।

+0

बहुत बहुत धन्यवाद। मैं एनिमेशन का उपयोग करने के लिए नया हूं और इससे एक टन मदद मिली! मैं इसे एक शॉट दूँगा! – Matt

+4

डब्ल्यूडब्ल्यूडीसी 2011 से [सत्र 421 - कोर एनीमेशन अनिवार्य] (https://developer.apple.com/videos/wwdc/2011/?id=421) वीडियो देखें। यह केवल एक घंटा है और यह ** अत्यंत ** है जानकारीपूर्ण। –

+0

बहुत बढ़िया उत्तर +1! – Till