एनीमेशन पूरा होने के बाद CABasicAnimation प्रारंभिक मूल्य पर रीसेट करता है


143

मैं एक काइलेयर को घुमा रहा हूं और एनीमेशन पूरा होने के बाद इसे अपने अंतिम स्थान पर रोकने की कोशिश कर रहा हूं।

लेकिन एनीमेशन के पूरा होने के बाद यह अपने प्रारंभिक स्थान पर रीसेट हो जाता है।

(xcode डॉक्स स्पष्ट रूप से कहते हैं कि एनीमेशन संपत्ति के मूल्य को अपडेट नहीं करेगा।)

किसी भी सुझाव यह कैसे प्राप्त करने के लिए।


1
यह उन अजीब एसओ प्रश्नों में से एक है जहां लगभग सभी उत्तर बिल्कुल गलत हैं - बस पूरी तरह से गलत । आप बहुत ही .presentation()"अंतिम, देखा" मूल्य प्राप्त करने के लिए देखते हैं। नीचे दिए गए सही उत्तरों की खोज करें, जो यह बताते हैं कि यह प्रस्तुति परत के साथ किया गया है।
फेटी

जवाबों:


285

यहाँ जवाब है, यह मेरे जवाब और कृष्णन का संयोजन है।

cabasicanimation.fillMode = kCAFillModeForwards;
cabasicanimation.removedOnCompletion = NO;

डिफ़ॉल्ट मान है kCAFillModeRemoved। (जो रीसेट व्यवहार आप देख रहे हैं।)


6
यह शायद सही उत्तर नहीं है - कृष्णन के उत्तर पर टिप्पणी देखें। आपको आमतौर पर इसकी आवश्यकता है और कृष्णन की; सिर्फ एक या दूसरे काम नहीं करेगा।
एडम

19
यह सही उत्तर नहीं है, नीचे देखें @Leslie Godwin का उत्तर।
मार्क इनग्राम

6
@ लेस्ली समाधान बेहतर है क्योंकि यह परत में अंतिम मूल्य को संग्रहीत करता है न कि एनीमेशन में।
मथिउ राउफ

1
जब आप NO। यदि आप "स्व" के लिए एनीमेशन प्रतिनिधि को सौंप रहे हैं, तो आपका उदाहरण एनीमेशन द्वारा बरकरार रखा जाएगा। इसलिए, यदि आप स्वयं द्वारा एनीमेशन को निकालना / नष्ट करना भूल जाते हैं, तो removeFromSuperview को कॉल करने के बाद, उदाहरण के लिए डीललॉक नहीं कहा जाएगा। आपके पास मेमोरी लीक होगी।
इमरगुंडुज़

1
यह 200+ बिंदु उत्तर पूरी तरह से, पूरी तरह से गलत है।
फेटी

83

RemoveOnCompletion के साथ समस्या यह है कि UI तत्व उपयोगकर्ता इंटरैक्शन की अनुमति नहीं देता है।

मैं तकनीक को एनीमेशन में FROM मान और ऑब्जेक्ट पर TO मान सेट करना है। एनीमेशन ऑटो स्टार्ट होने से पहले TO मान को भर देगा, और जब इसे हटा दिया जाएगा तो यह सही अवस्था में ऑब्जेक्ट को छोड़ देगा।

// fade in
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath: @"opacity"];
alphaAnimation.fillMode = kCAFillModeForwards;

alphaAnimation.fromValue = NUM_FLOAT(0);
self.view.layer.opacity = 1;

[self.view.layer addAnimation: alphaAnimation forKey: @"fade"];

7
यदि आप देरी शुरू करते हैं तो काम नहीं करता है। उदा। alphaAnimation.beginTime = 1; इसके अलावा, fillModeजहाँ तक मैं बता सकता हूँ ... की जरूरत नहीं है?
सैम

अन्यथा, यह एक अच्छा जवाब है, स्वीकार किए गए उत्तर एनीमेशन पूरा होने के बाद आपको एक मान बदलने नहीं देंगे ...
सैम

2
मुझे यह भी ध्यान देना चाहिए कि beginTimeसापेक्ष नहीं है, आपको इसका उपयोग करना चाहिए जैसे:CACurrentMediaTime()+1;
सैम

यह 'इसका' नहीं '' है '' - it-not-its.info। क्षमा करें यदि यह सिर्फ एक टाइपो था।
फतुहोकू

6
सही उत्तर, लेकिन ज़रूरत नहीं है fillMode, अधिक स्पष्ट
तरीके से तड़कने से बचाएं

16

निम्नलिखित संपत्ति सेट करें:

animationObject.removedOnCompletion = NO;

@ नीलेश: अपना जवाब स्वीकार करो। जो आपके उत्तर पर अन्य SO उपयोगकर्ताओं को विश्वास दिलाएगा।
कृष्णायन

7
मुझे दोनों .fillMode = kCAFillModeForwards और .removedOnCompletion = NO का उपयोग करना पड़ा। धन्यवाद!
विलियम रस्ट

12

बस इसे अपने कोड के अंदर रखें

CAAnimationGroup *theGroup = [CAAnimationGroup animation];

theGroup.fillMode = kCAFillModeForwards;

theGroup.removedOnCompletion = NO;

12

आप बस के प्रमुख सेट कर सकते हैं CABasicAnimationकरने के लिए positionजब आप इसे परत में जोड़ें। ऐसा करने से, यह रन लूप में वर्तमान पास के लिए स्थिति पर किए गए निहित एनीमेशन को ओवरराइड करेगा।

CGFloat yOffset = 30;
CGPoint endPosition = CGPointMake(someLayer.position.x,someLayer.position.y + yOffset);

someLayer.position = endPosition; // Implicit animation for position

CABasicAnimation * animation =[CABasicAnimation animationWithKeyPath:@"position.y"]; 

animation.fromValue = @(someLayer.position.y);
animation.toValue = @(someLayer.position.y + yOffset);

[someLayer addAnimation:animation forKey:@"position"]; // The explicit animation 'animation' override implicit animation

आपके पास 2011 Apple WWDC वीडियो सत्र 421 के बारे में अधिक जानकारी हो सकती है - कोर एनीमेशन अनिवार्य (वीडियो के बीच में)


1
ऐसा करने का यह सही तरीका लगता है। एनिमेशन स्वाभाविक रूप से हटा दिया गया है (डिफ़ॉल्ट), और एक बार एनीमेशन पूरा हो जाने के बाद, यह एनीमेशन शुरू होने से पहले निर्धारित मूल्यों पर लौटता है, विशेष रूप से यह लाइन someLayer.position = endPosition;:। और WWDC को रेफरी के लिए धन्यवाद!
bauerMusic

10

एक CALayer में एक मॉडल परत और एक प्रस्तुति परत होती है। एक एनीमेशन के दौरान, प्रस्तुति परत मॉडल से स्वतंत्र रूप से अपडेट होती है। जब एनीमेशन पूरा हो जाता है, तो प्रस्तुति परत को मॉडल से मूल्य के साथ अपडेट किया जाता है। यदि आप एनीमेशन समाप्त होने के बाद एक झटके से बचना चाहते हैं, तो कुंजी दो परतों को सिंक में रखना है।

यदि आप अंतिम मूल्य जानते हैं, तो आप सीधे मॉडल सेट कर सकते हैं।

self.view.layer.opacity = 1;

लेकिन अगर आपके पास एक एनीमेशन है जहां आप अंतिम स्थिति नहीं जानते हैं (उदाहरण के लिए एक धीमी गति से फीका जिसे उपयोगकर्ता रोक सकता है और फिर रिवर्स कर सकता है), तो आप वर्तमान मूल्य को खोजने के लिए प्रस्तुति परत को सीधे क्वेरी कर सकते हैं और फिर मॉडल को अपडेट कर सकते हैं।

NSNumber *opacity = [self.layer.presentationLayer valueForKeyPath:@"opacity"];
[self.layer setValue:opacity forKeyPath:@"opacity"];

प्रस्तुति परत से मान खींचना स्केलिंग या रोटेशन कीपैड के लिए भी विशेष रूप से उपयोगी है। (जैसे transform.scale, transform.rotation)


8

का उपयोग किए बिना removedOnCompletion

आप इस तकनीक को आजमा सकते हैं:

self.animateOnX(item: shapeLayer)

func animateOnX(item:CAShapeLayer)
{
    let endPostion = CGPoint(x: 200, y: 0)
    let pathAnimation = CABasicAnimation(keyPath: "position")
    //
    pathAnimation.duration = 20
    pathAnimation.fromValue = CGPoint(x: 0, y: 0)//comment this line and notice the difference
    pathAnimation.toValue =  endPostion
    pathAnimation.fillMode = kCAFillModeBoth

    item.position = endPostion//prevent the CABasicAnimation from resetting item's position when the animation finishes

    item.add(pathAnimation, forKey: nil)
}

3
यह सबसे अच्छा कामकाज का जवाब लगता है
रामबोस 14

माना। नोट @ ayman की टिप्पणी है कि आपको प्रारंभ मान के लिए .fromValue गुण निर्धारित करना चाहिए । अन्यथा, आपने मॉडल में प्रारंभ मूल्य को ओवरराइट कर दिया होगा, और एनीमेशन होगा।
जेफ कोलियर

यह एक और @ जैसन-मूर का उत्तर स्वीकृत उत्तर से बेहतर है। स्वीकृत उत्तर में एनीमेशन को रोक दिया गया है लेकिन नया मूल्य वास्तव में संपत्ति पर सेट नहीं है। इससे अवांछित व्यवहार हो सकता है।
लाका

8

तो मेरी समस्या यह थी कि मैं पैन इशारे पर किसी वस्तु को घुमाने की कोशिश कर रहा था और इसलिए मेरे पास प्रत्येक चाल पर कई समान एनिमेशन थे। मेरे पास दोनों थे fillMode = kCAFillModeForwardsऔर isRemovedOnCompletion = falseइससे मदद नहीं मिली। मेरे मामले में, मुझे यह सुनिश्चित करना था कि हर बार जब मैं एक नया एनीमेशन जोड़ता हूँ तो एनीमेशन कुंजी अलग होती है :

let angle = // here is my computed angle
let rotate = CABasicAnimation(keyPath: "transform.rotation.z")
rotate.toValue = angle
rotate.duration = 0.1
rotate.isRemovedOnCompletion = false
rotate.fillMode = CAMediaTimingFillMode.forwards

head.layer.add(rotate, forKey: "rotate\(angle)")

7

बस सेटिंग fillModeऔर removedOnCompletionमेरे लिए काम नहीं किया। CABasicAnimation ऑब्जेक्ट के नीचे सभी गुण सेट करके मैंने समस्या हल की :

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.duration = 0.38f;
ba.fillMode = kCAFillModeForwards;
ba.removedOnCompletion = NO;
ba.autoreverses = NO;
ba.repeatCount = 0;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.85f, 0.85f, 1.0f)];
[myView.layer addAnimation:ba forKey:nil];

यह कोड myView85% अपने आकार (3 डी आयाम अनलेडेड) में बदल जाता है।


7

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

सीधे शब्दों में स्थापित करने removedOnCompletionके लिए NOसाधन एनीमेशन नहीं निकाला जाएगा और अपशिष्ट स्मृति। इसके अलावा, मॉडल परत और प्रस्तुति परत किसी भी अधिक तुल्यकालिक नहीं होगी, जिससे संभावित कीड़े हो सकते हैं।

इसलिए संपत्ति के मॉडल को सीधे अंतिम मूल्य पर अद्यतन करने के लिए यह एक बेहतर समाधान होगा।

self.view.layer.opacity = 1;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];

यदि उपरोक्त कोड की पहली पंक्ति के कारण कोई अंतर्निहित एनीमेशन है, तो बंद करने का प्रयास करें:

[CATransaction begin];
[CATransaction setDisableActions:YES];
self.view.layer.opacity = 1;
[CATransaction commit];

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];

धन्यवाद! यह वास्तव में स्वीकृत उत्तर होना चाहिए, क्योंकि यह सही ढंग से इस कार्य को बनाने के लिए एक बैंड-सहायता का उपयोग करने के बजाय इस कार्यक्षमता की व्याख्या करता है
bplattenburg

6

यह काम:

let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3

someLayer.opacity = 1 // important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")

कोर एनीमेशन एक 'प्रेजेंटेशन लेयर' को दिखाता है, जो एनिमेशन के दौरान आपकी सामान्य लेयर के ऊपर होता है। तो अस्पष्टता (या जो कुछ भी) सेट करें कि आप क्या देखना चाहते हैं जब एनीमेशन खत्म हो जाता है और प्रस्तुति परत चली जाती है। लाइन पर ऐसा करने से पहले आप एनीमेशन जोड़ने के एक झिलमिलाहट से बचने के लिए जब यह पूरा करता है।

यदि आप देरी करना चाहते हैं, तो निम्न कार्य करें:

let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3
animation.beginTime = someLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) + 1
animation.fillMode = kCAFillModeBackwards // So the opacity is 0 while the animation waits to start.

someLayer.opacity = 1 // <- important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")

अंत में, यदि आप 'हटाए गएOnCompletion = false' का उपयोग करते हैं, तो यह CAAimimations को लीक कर देगा, जब तक कि परत को अंततः निपटाया नहीं जाता - बचें।


शानदार जवाब, शानदार टिप्स। हटाए गए कमेंट कमेंट के लिए धन्यवाद।
iWheelBuy

4

@ लेस्ली गॉडविन का जवाब वास्तव में अच्छा नहीं है, "self.view.layer.opacity = 1;" तुरंत किया जाता है (इसमें लगभग एक सेकंड का समय लगता है), कृपया अल्फाएनिमेशन। अवधि को 10.0 तक ठीक करें, अगर आपको संदेह है। आपको इस लाइन को हटाना होगा।

इसलिए, जब आप fillMode को kCAFillModeForwards में निकालते हैं और NO को हटा दिया जाता है, तो आप एनीमेशन को परत में ही रहने देते हैं। यदि आप एनीमेशन प्रतिनिधि को ठीक करते हैं और कुछ इस तरह की कोशिश करते हैं:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
 [theLayer removeAllAnimations];
}

... जिस समय आप इस लाइन को निष्पादित करते हैं, उस समय यह परत तुरंत ठीक हो जाती है। यह वही है जिससे हम बचना चाहते थे।

इससे एनीमेशन को हटाने से पहले आपको परत की संपत्ति को ठीक करना होगा। इसे इस्तेमाल करे:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
     if([anim isKindOfClass:[CABasicAnimation class] ]) // check, because of the cast
    {
        CALayer *theLayer = 0;
        if(anim==[_b1 animationForKey:@"opacity"])
            theLayer = _b1; // I have two layers
        else
        if(anim==[_b2 animationForKey:@"opacity"])
            theLayer = _b2;

        if(theLayer)
        {
            CGFloat toValue = [((CABasicAnimation*)anim).toValue floatValue];
            [theLayer setOpacity:toValue];

            [theLayer removeAllAnimations];
        }
    }
}

1

ऐसा लगता है कि हटाए गए हटाए गए फ्लैगशिप फ्लैग सेट को गलत और fillMode को kCAFillModeForwards पर सेट करना मेरे लिए काम नहीं करता है।

जब मैं एक लेयर पर नया एनीमेशन लागू करता हूं, तो एक एनिमेटिंग ऑब्जेक्ट अपनी प्रारंभिक स्थिति में रहता है और फिर उस स्थिति से एनिमेट करता है। अतिरिक्त रूप से क्या करना है, नए एनिमेशन सेट करने से पहले मॉडल की परत की वांछित संपत्ति को उसकी प्रस्तुति परत की संपत्ति के अनुसार सेट करना है:

someLayer.path = ((CAShapeLayer *)[someLayer presentationLayer]).path;
[someLayer addAnimation:someAnimation forKey:@"someAnimation"];

0

सबसे आसान समाधान निहित एनिमेशन का उपयोग करना है। यह आपके लिए उस परेशानी से निपटने में मदद करेगा:

self.layer?.backgroundColor = NSColor.red.cgColor;

यदि आप उदाहरण के लिए अवधि को अनुकूलित करना चाहते हैं, तो आप इसका उपयोग कर सकते हैं NSAnimationContext:

    NSAnimationContext.beginGrouping();
    NSAnimationContext.current.duration = 0.5;
    self.layer?.backgroundColor = NSColor.red.cgColor;
    NSAnimationContext.endGrouping();

नोट: यह केवल macOS पर परीक्षण किया गया है।

मैंने शुरू में ऐसा करते समय कोई एनीमेशन नहीं देखा था। समस्या यह है कि एक दृश्य-समर्थित परत की परत चेतन को निहित नहीं करती है । इसे हल करने के लिए, सुनिश्चित करें कि आप स्वयं एक परत जोड़ते हैं (परत-समर्थित पर दृश्य सेट करने से पहले)।

यह कैसे करना है एक उदाहरण:

override func awakeFromNib() {
    self.layer = CALayer();
    //self.wantsLayer = true;
}

उपयोग करने self.wantsLayerसे मेरे परीक्षण में कोई फर्क नहीं पड़ा, लेकिन इसके कुछ दुष्प्रभाव हो सकते हैं जो मुझे नहीं पता।


0

यहाँ खेल के मैदान से एक नमूना है:

import PlaygroundSupport
import UIKit

let resultRotation = CGFloat.pi / 2
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 300.0))
view.backgroundColor = .red

//--------------------------------------------------------------------------------

let rotate = CABasicAnimation(keyPath: "transform.rotation.z") // 1
rotate.fromValue = CGFloat.pi / 3 // 2
rotate.toValue = resultRotation // 3
rotate.duration = 5.0 // 4
rotate.beginTime = CACurrentMediaTime() + 1.0 // 5
// rotate.isRemovedOnCompletion = false // 6
rotate.fillMode = .backwards // 7

view.layer.add(rotate, forKey: nil) // 8
view.layer.setAffineTransform(CGAffineTransform(rotationAngle: resultRotation)) // 9

//--------------------------------------------------------------------------------

PlaygroundPage.current.liveView = view
  1. एक एनीमेशन मॉडल बनाएँ
  2. एनीमेशन की शुरुआत की स्थिति निर्धारित करें (इसे छोड़ दिया जा सकता है, यह आपकी वर्तमान परत लेआउट पर निर्भर करता है)
  3. एनीमेशन की अंतिम स्थिति निर्धारित करें
  4. एनीमेशन अवधि सेट करें
  5. एक सेकंड के लिए देरी एनीमेशन
  6. एनीमेशन समाप्त होने के बाद कोर एनिमेशन falseको सेट न isRemovedOnCompletionहोने दें
  7. यहां ट्रिक का पहला भाग है - आप अपने एनिमेशन को स्टार्ट पोजीशन पर रखने के लिए कोर एनिमेशन से कहते हैं (आपने स्टेप # 2 में सेट किया है) एनिमेशन शुरू होने से पहले ही - इसे समय में पीछे की ओर बढ़ाएं
  8. तैयार एनीमेशन ऑब्जेक्ट को कॉपी करें, इसे परत में जोड़ें और देरी के बाद एनीमेशन शुरू करें (आप चरण # 5 में सेट करें)
  9. दूसरा भाग परत की सही अंत स्थिति सेट करने के लिए है - एनीमेशन हटाए जाने के बाद आपकी परत सही जगह पर दिखाई जाएगी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.