2012-02-27 20 views
11

पर मैं एक UIImageView वस्तु एक बेज़ियर मार्ग के किनारे हिलाने की स्थिति की मुख्य फ्रेम एनीमेशन के साथ प्रयोग कर रहा हूँ। यह तस्वीर एनीमेशन से पहले प्रारंभिक स्थिति दिखाती है।आईओएस CAKeyframeAnimation rotationMode UIImageView

Initial state

जब: - ब्लू लाइन मार्ग है शुरू में सीधे ऊपर जा रहा है, हल्के हरे रंग बॉक्स प्रारंभिक सीमांकन बॉक्स या छवि, और गहरे हरे रंग "भूत" छवि है कि मैं आगे बढ़ रहा है मैं nil को rotationMode सेट के साथ एनीमेशन शुरू, उम्मीद के रूप में छवि पथ के माध्यम से सभी तरह से एक ही उन्मुखीकरण रहता है।

लेकिन जब मैं रोटेशन मोड के साथ एनीमेशन को बंद करता हूं तो kCAAnimationRotateAuto पर सेट होता है, तो छवि तुरंत 90 डिग्री विरोधी घड़ी की दिशा में घूमती है और पथ के माध्यम से इस अभिविन्यास को सभी तरह से रखती है। जब यह पथ यह सही ओरिएंटेशन में redraws के अंत तक पहुँच जाता है

enter image description here

मैं भोलेपन उम्मीद कर रहा था कि rotationMode करने के लिए छवि बोध होता है (अच्छी तरह से यह वास्तव में UIImageView है कि मैं अंतिम स्थान में उसका स्थान से पता चलता) पथ की और सामान्य करने के लिए नहीं स्पर्श, खासकर जब CAKeyframeAnimation rotationMode राज्य

तय करता है कि वस्तुओं मार्ग के किनारे एनिमेट के लिए एप्पल डॉक्स बारी बारी पथ स्पर्श मैच के लिए।

तो क्या यहाँ समाधान है? क्या मुझे छवि को 90 डिग्री दक्षिणावर्त से पूर्व-घुमाएं? या क्या ऐसा कुछ है जिसे मैं याद कर रहा हूं?

आपकी मदद के लिए धन्यवाद।


संपादित 2 एन डी मार्च

मैं की तरह एक Affine रोटेशन का उपयोग पथ एनीमेशन से पहले एक रोटेशन कदम कहा:

theImage.transform = CGAffineTransformRotate(theImage.transform,90.0*M_PI/180); 

और फिर पथ एनीमेशन के बाद, साथ रोटेशन को रीसेट:

theImage.transform = CGAffineTransformIdentity; 

यह वें बनाता है ई छवि अपेक्षित तरीके से पथ का पालन करें। हालांकि अब मैं छवि झिलमिलाहट की एक अलग समस्या में चल रहा हूँ। मैं पहले से ही इस अतः प्रश्न में अस्थिर मुद्दा के समाधान के लिए देख रहा हूँ:

iOS CAKeyFrameAnimation scaling flickers at animation end

तो अब मैं नहीं जानता कि यदि मैं चीजों को बेहतर या बदतर बना दिया है!


संपादित मार्च 12

जबकि कालेब ने कहा कि हाँ, मैं अपने चित्र को घुमाने के पूर्व की क्या ज़रूरत थी, रोब कोड के एक भयानक पैकेज है कि लगभग पूरी तरह से मेरी समस्याओं को हल प्रदान की है। केवल बात यह है कि रोब मेरी संपत्ति के लिए मुआवजा दिया गया था नहीं किया क्षैतिज अभिविन्यास से एक ऊर्ध्वाधर बल्कि साथ तैयार किया जा रहा है, इस प्रकार अभी भी एनीमेशन करने से पहले 90 डिग्री से उन्हें preRotate करने की जरूरत पड़ेगी।लेकिन हे, इसकी केवल निष्पक्ष मेरे पास है कि चल रहा चीजों को पाने के लिए काम से कुछ करते हैं।

तो रोब के समाधान के लिए अपने मामूली परिवर्तन सूट करने के लिए मेरी आवश्यकताएँ हैं:

  1. जब मैं UIView जोड़ने के लिए, मैं पूर्व यह घुमाएँ निहित रोटेशन की स्थापना द्वारा जोड़ा rotationMode मुकाबला करने के लिए:

    theImage.transform = CGAffineTransformMakeRotation (90 * M_PI/180.0);

  2. मैं एनीमेशन के अंत में है कि रोटेशन रखने की जरूरत है, तो बस देखने का एक नया पैमाने कारक के साथ बदलने के बाद पूरा होने के ब्लॉक परिभाषित किया गया है नष्ट करने के बजाय, मैं पैमाने वर्तमान के आधार पर निर्माण को बदलने:

    theImage.transform = CGAffineTransformScale (theImage.transform, scaleFactor, scaleFactor);

और मुझे बस अपनी छवि को पथ के अनुसरण के लिए प्राप्त करने के लिए करना है जैसा कि मैंने उम्मीद की थी!


संपादित मार्च 22

मैं सिर्फ GitHub एक डेमो परियोजना है कि एक बेज़ियर मार्ग के किनारे एक वस्तु की चलती बंद से पता चलता पर अपलोड की गई। कोड पर PathMove

मैं भी Moving objects along a bezier path in iOS

+0

यह मैं बाहर बहुत मदद की! धन्यवाद! – RyanG

उत्तर

8

यहां मुद्दा यह है कि कोर एनीमेशन का ऑटोरोटेशन पथ के टेंगेंट के समानांतर दृश्य के क्षैतिज धुरी को रखता है। यही कारण है कि यह सिर्फ कैसे काम करता है।

आप अपने दृश्य के ऊर्ध्वाधर अक्ष के बजाय पथ के स्पर्श का पालन करने, देखने यदि आप वर्तमान में कर रहे हैं के रूप में की सामग्री को घूर्णन चाहते हैं करने के लिए उचित बात है।

+0

ठीक है .. तो यह है कि यह कैसे काम करता है। मैं उसे एक दस्तावेज़ीकरण मुद्दे पर डाल दूंगा। लेकिन जब मैं एफ़िन रोटेशन का उपयोग करता हूं तो मैं ऊपर दिए गए संपादन में उल्लिखित मेरे अन्य SO प्रश्न पर इस मुद्दे पर चलता हूं। क्या मुझे एफ़िन रोटेशन के बजाय प्री-रोटेशन करने के लिए सीए का उपयोग करना चाहिए? या क्या उस दूसरे प्रश्न में चीजें हैं कि मैं गलत कर रहा हूं? –

+0

मैंने कभी परीक्षण नहीं किया है कि CGAffineTransform का उपयोग करने से कोई फर्क पड़ता है, इसलिए मैं उससे बात नहीं कर सकता। सहजता से, चूंकि आप पहले से ही कोर एनीमेशन के साथ परतों के संदर्भ में सोच रहे हैं, इसके बजाय कोर एनीमेशन के परिवर्तन का उपयोग करना समझ में आ सकता है: '[theView.layer setValue: [NSNumber numberWithFloat: M_PI/2] forKey: @ "transform.rotation" ]; '। लेकिन ऐसा लगता है कि CGAffineTransform को कैलियर की रूपांतरण क्षमता का उपयोग करके कार्यान्वित किया जाना चाहिए, इसलिए इससे कोई फर्क नहीं पड़ता। मैं अभी समय पर छोटा हूं, लेकिन जब मुझे मौका मिलता है तो आपके दूसरे प्रश्न पर नजर डालेंगे। – Caleb

+0

मेरे दूसरे प्रश्न के उत्तर से मैं कहूंगा कि कोर एनीमेशन के साथ घूर्णन करने का तरीका है। जब मुझे उस प्रश्न के लिए ठोस जवाब मिलता है तो मैं उसी तरह घूर्णन से निपटने की कोशिश करूंगा। –

3

पर अपने ब्लॉग में इसके बारे में लिखा पाया जा सकता है Yow है कि आप "rotationMode हाँ करने के लिए सेट" के साथ एनीमेशन शुरू उल्लेख है, लेकिन प्रलेखन कहा गया है कि rotationMode सेट किया जाना चाहिए

इन स्थिरांक rotationMode संपत्ति द्वारा किया जाता है: एक NSString ...

विशेष रूप से इस्तेमाल करते हैं।

NSString * स्थिरांक kCAAnimationRotateAuto

NSString * स्थिरांक kCAAnimationRotateAutoReverse

आप स्थापित करने की कोशिश की है:

keyframe.animationMode = kCAAnimationRotateAuto;

प्रलेखन राज्यों:

kCAAnimationRotateAuto: वस्तुओं पथ की स्पर्श रेखा पर यात्रा करते हैं।

+0

यह केवल प्रश्न पाठ था जो गलत था, मैं 'nil' और 'kCAAnimationRotateAuto' का उपयोग कर रहा हूं। इसे प्रतिबिंबित करने के लिए बस प्रश्न संपादित किया। –

6

यहाँ तुम क्या झिलमिलाहट को खत्म करने के कुछ जानना चाहते हैं:

  • रूप कालेब तरह की ने कहा, कोर एनिमेशन अपने परत घूमता है, ताकि उसके सकारात्मक एक्स अक्ष अपने पथ के स्पर्श के साथ निहित है। आपको इसके साथ अपनी छवि का "प्राकृतिक" अभिविन्यास कार्य करने की आवश्यकता है। तो, मान है कि आपके उदाहरण छवियों में एक हरे रंग अंतरिक्ष यान है, तो आप सही जब यह करने के लिए लागू रोटेशन नहीं है को इंगित करने के अंतरिक्ष यान की जरूरत है।

  • एक को बदलने कि रोटेशन शामिल स्थापना रोटेशन `kCAAnimationRotateAuto 'द्वारा लागू हस्तक्षेप करता है। एनीमेशन लागू करने से पहले आपको अपने ट्रांसफॉर्म से रोटेशन को हटाना होगा।

  • बेशक आप परिवर्तन पुन: लागू करने के लिए जब एनीमेशन पूरा करता है की जरूरत है इसका मतलब है कि के

    । और निश्चित रूप से आप छवि की उपस्थिति में किसी भी झिलमिलाहट को देखे बिना ऐसा करना चाहते हैं। यह मुश्किल नहीं है, लेकिन वहां कुछ गुप्त सॉस शामिल हैं, जिन्हें मैं नीचे समझाता हूं।

  • संभवतः आप अपनी स्पेसशिप पथ के स्पर्शरेखा के साथ इंगित करना शुरू करना चाहते हैं, भले ही स्पेसशिप अभी भी एनिमेटेड न हो। अपने अंतरिक्ष यान छवि सही करने के लिए इशारा कर रही है, लेकिन अपने पथ ऊपर जाता है, तो आप छवि के बदलने एक 90 डिग्री रोटेशन शामिल करने के लिए सेट करना होगा। लेकिन शायद आप उस घूर्णन को हार्डकोड नहीं करना चाहते हैं - इसके बजाय आप पथ को देखना चाहते हैं और इसके शुरुआती स्पर्शरेखा को समझना चाहते हैं।

मैं यहां कुछ महत्वपूर्ण कोड दिखाऊंगा। आप my test project on github पा सकते हैं। आपको इसे डाउनलोड करने और इसे आजमाने में कुछ उपयोग मिल सकता है। एनीमेशन देखने के लिए बस हरे "स्पेसशिप" पर टैप करें।

तो, मेरे परीक्षण प्रोजेक्ट में, मैंने को animate: नामक एक क्रिया से जोड़ा है। जब आप इसे स्पर्श करते हैं, तो छवि आकृति 8 के आधे भाग और आकार में युगल के साथ चलता है। जब आप इसे फिर से स्पर्श करते हैं, तो छवि आकृति 8 के दूसरे भाग (प्रारंभिक स्थिति पर वापस) के साथ चलता है, और इसके मूल आकार पर वापस आ जाता है। दोनों एनिमेशन kCAAnimationRotateAuto उपयोग करते हैं, तो पथ के स्पर्श के साथ छवि अंक।

यहाँ animate:, जहां मैं यह पता लगाने की क्या पथ, पैमाने, और गंतव्य बिंदु छवि पर समाप्त होना चाहिए की शुरुआत है:

- (IBAction)animate:(id)sender { 
    UIImageView* theImage = self.imageView; 

    UIBezierPath *path = _isReset ? _path0 : _path1; 
    CGFloat newScale = 3 - _currentScale; 

    CGPoint destination = [path currentPoint]; 

तो, पहली बात मैं क्या करने की जरूरत से किसी भी रोटेशन को दूर है छवि के, को बदलने के बाद से के रूप में मैं उल्लेख किया है, यह kCAAnimationRotateAuto के साथ हस्तक्षेप करेगा:

// Strip off the image's rotation, because it interferes with `kCAAnimationRotateAuto`. 
    theImage.transform = CGAffineTransformMakeScale(_currentScale, _currentScale); 

इसके बाद, मैं एक UIView एनीमेशन ब्लॉक में जाने ताकि सिस्टम छवि को देखने के लिए एनिमेशन लागू होंगे:

[UIView animateWithDuration:3 animations:^{ 

मैं पद के लिए मुख्य-फ़्रेम एनीमेशन बना सकते हैं और इसके गुणों की एक जोड़ी सेट:

 // Prepare my own keypath animation for the layer position. 
     // The layer position is the same as the view center. 
     CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
     positionAnimation.path = path.CGPath; 
     positionAnimation.rotationMode = kCAAnimationRotateAuto; 

अगला एनीमेशन के अंत में झिलमिलाहट रोकने के लिए गुप्त सॉस है। याद है कि एनिमेशन नहीं प्रभाव "मॉडल परत" है कि आप (इस मामले में theImage.layer) करने के लिए उन्हें संलग्न के गुणों से करते हैं। इसके बजाय, वे "प्रेजेंटेशन लेयर" के गुणों को अपडेट करते हैं, जो स्क्रीन पर वास्तव में क्या दर्शाता है।

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

 positionAnimation.removedOnCompletion = NO; 
     [CATransaction setCompletionBlock:^{ 
      CGAffineTransform finalTransform = [theImage.layer.presentationLayer affineTransform]; 
      [theImage.layer removeAnimationForKey:positionAnimation.keyPath]; 
      theImage.transform = finalTransform; 
     }]; 

अब जब मैंने पूरा करने वाला ब्लॉक स्थापित किया है, तो मैं वास्तव में दृश्य गुणों को बदल सकता हूं। जब मैं ऐसा करता हूं तो सिस्टम स्वचालित रूप से परत पर एनिमेशन संलग्न करेगा।

 // UIView will add animations for both of these changes. 
     theImage.transform = CGAffineTransformMakeScale(newScale, newScale); 
     theImage.center = destination; 

मैं अपने-फ़्रेम एनीमेशन के लिए अपने आप जोड़े स्थिति एनीमेशन से कुछ प्रमुख गुण कॉपी:

 // Copy properties from UIView's animation. 
     CAAnimation *autoAnimation = [theImage.layer animationForKey:positionAnimation.keyPath]; 
     positionAnimation.duration = autoAnimation.duration; 
     positionAnimation.fillMode = autoAnimation.fillMode; 

और अंत में मैं-फ़्रेम एनीमेशन के साथ अपने आप जोड़े स्थिति एनीमेशन बदल देते हैं:

 // Replace UIView's animation with my animation. 
     [theImage.layer addAnimation:positionAnimation forKey:positionAnimation.keyPath]; 
    }]; 

डबल-अंत में मैं छवि दृश्य में परिवर्तन को दर्शाने के लिए अपने आवृत्ति चर अद्यतन करता हूं:

_currentScale = newScale; 
    _isReset = !_isReset; 
} 

यह छवि दृश्य को एनिमेट करने के लिए कोई झिलमिलाहट नहीं है।

और अब स्टीव जॉब्स कहेंगे, एक अंतिम बात। जब मैं दृश्य लोड करता हूं, तो मुझे छवि दृश्य के परिवर्तन को सेट करने की आवश्यकता होती है ताकि यह पहले पथ के स्पर्शरेखा के साथ इंगित करने के लिए घुमाया जा सके जिसे मैं इसे एनिमेट करने के लिए उपयोग करूंगा। मुझे लगता है कि एक विधि में reset नामित कार्य करें:

- (void)reset { 
    self.imageView.center = _path1.currentPoint; 
    self.imageView.transform = CGAffineTransformMakeRotation(startRadiansForPath(_path0)); 
    _currentScale = 1; 
    _isReset = YES; 
} 
बेशक

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

I ' मीटर सिर्फ कोड भावी पीढ़ी के लिए, विवरण के बिना यहां डंप करने के लिए जा:

typedef struct { 
    CGPoint p0; 
    CGPoint p1; 
    CGPoint firstPointOfCurrentSubpath; 
    CGPoint currentPoint; 
    BOOL p0p1AreSet : 1; 
} PathState; 

static inline void updateStateWithMoveElement(PathState *state, CGPathElement const *element) { 
    state->currentPoint = element->points[0]; 
    state->firstPointOfCurrentSubpath = state->currentPoint; 
} 

static inline void updateStateWithPoints(PathState *state, CGPoint p1, CGPoint currentPoint) { 
    if (!state->p0p1AreSet) { 
     state->p0 = state->currentPoint; 
     state->p1 = p1; 
     state->p0p1AreSet = YES; 
    } 
    state->currentPoint = currentPoint; 
} 

static inline void updateStateWithPointsElement(PathState *state, CGPathElement const *element, int newCurrentPointIndex) { 
    updateStateWithPoints(state, element->points[0], element->points[newCurrentPointIndex]); 
} 

static void updateStateWithCloseElement(PathState *state, CGPathElement const *element) { 
    updateStateWithPoints(state, state->firstPointOfCurrentSubpath, state->firstPointOfCurrentSubpath); 
} 

static void updateState(void *info, CGPathElement const *element) { 
    PathState *state = info; 
    switch (element->type) { 
     case kCGPathElementMoveToPoint: return updateStateWithMoveElement(state, element); 
     case kCGPathElementAddLineToPoint: return updateStateWithPointsElement(state, element, 0); 
     case kCGPathElementAddQuadCurveToPoint: return updateStateWithPointsElement(state, element, 1); 
     case kCGPathElementAddCurveToPoint: return updateStateWithPointsElement(state, element, 2); 
     case kCGPathElementCloseSubpath: return updateStateWithCloseElement(state, element); 
    } 
} 

CGFloat startRadiansForPath(UIBezierPath *path) { 
    PathState state; 
    memset(&state, 0, sizeof state); 
    CGPathApply(path.CGPath, &state, updateState); 
    return atan2f(state.p1.y - state.p0.y, state.p1.x - state.p0.x); 
} 
+0

इस उत्तर के विवरण के लिए धन्यवाद। मैं वास्तव में इसकी प्रशंसा करता हूँ। हालांकि, मैंने आपके प्रोजेक्ट में जो देखा, उससे आपने संपत्ति को सही दिशा में इंगित किया, इसे डिस्प्ले पर पथ टेंगेंट पर घुमाया और एनीमेशन पर उस घूर्णन को हटा दिया। मेरी स्थिति में मुझे उन संपत्तियों का उपयोग करने की आवश्यकता है जो प्रभावी ढंग से पूर्व-खींचे गए हैं, ताकि एनीमेशन पर उन्हें 90 डिग्री से घूमने की आवश्यकता हो और फिर पथ के साथ स्थानांतरित हो जाएं। मैं बाद में अपने समाधान के साथ खेलूँगा यह देखने के लिए कि अब मैं अपने लिए क्या समझ सकता हूं कि आपने मुझे एक अच्छा प्रारंभिक बिंदु दिया है। एक बार फिर धन्यवाद। –

+0

मुझे आज सुबह मुझे यह समझने में काफी समय नहीं लगा कि मुझे अपनी आवश्यकताओं के अनुरूप क्या करना है। आपके द्वारा सहायता में किए गए प्रयासों के लिए धन्यवाद! –