2012-10-16 10 views
30

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

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { 
UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; 

// Assign the new layout attributes 
attributes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5); 
attributes.alpha = 0; 

return attributes; 

}

-(UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ 

UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; 

// Assign the new layout attributes 
attributes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5); 
attributes.alpha = 0; 

return attributes; 

}

+0

एप्पल के प्रलेखन अनुसार, अब मान्य नहीं है "जब लेआउट परिवर्तन एनिमेट, एनीमेशन समय और मानकों संग्रह दृश्य द्वारा नियंत्रित कर रहे हैं।" यह setCollectionView के संदर्भ में है: एनिमेटेड: विधि, लेकिन मुझे संदेह है कि संग्रह दृश्य की सीमाओं को संशोधित करने के लिए भी यही सच है। क्षमा करें मैं और मदद नहीं कर सकता, मैं एक ही समस्या पर फंस गया हूँ। मुझे संदेह है कि उत्तर UICollectionView ऑब्जेक्ट के भीतर कहीं भी स्थित है। – Ash

उत्तर

22

हैक कि answer by gavrix आप नए संपत्ति CABasicAnimation *transformAnimation साथ UICollectionViewLayoutAttributes उपवर्ग से एक उपयुक्त अवधि के साथ कस्टम परिवर्तन बना सकते हैं और initialLayoutAttributesForAppearingItemAtIndexPath में विशेषताओं के लिए असाइन करें, फिर आवश्यकतानुसार में UICollectionViewCell गुण लागू हो सकते हैं में प्रस्तावित किया गया था के बिना समस्या का समाधान करने के लिए:

@interface AnimationCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes 
@property (nonatomic, strong) CABasicAnimation *transformAnimation; 
@end 

@implementation AnimationCollectionViewLayoutAttributes 
- (id)copyWithZone:(NSZone *)zone 
{ 
    AnimationCollectionViewLayoutAttributes *attributes = [super copyWithZone:zone]; 
    attributes.transformAnimation = _transformAnimation; 
    return attributes; 
} 

- (BOOL)isEqual:(id)other { 
    if (other == self) { 
     return YES; 
    } 
    if (!other || ![[other class] isEqual:[self class]]) { 
     return NO; 
    } 
    if ([((AnimationCollectionViewLayoutAttributes *) other) transformAnimation] != [self transformAnimation]) { 
     return NO; 
    } 

    return YES; 
} 
@end 

लेआउट कक्षा में

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { 
    AnimationCollectionViewLayoutAttributes* attributes = (AnimationCollectionViewLayoutAttributes*)[super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; 

    CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; 
    transformAnimation.duration = 1.0f; 
    CGFloat height = [self collectionViewContentSize].height; 

    transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, 2*height, height)]; 
    transformAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, attributes.bounds.origin.y, 0)]; 
    transformAnimation.removedOnCompletion = NO; 
    transformAnimation.fillMode = kCAFillModeForwards; 
    attributes.transformAnimation = transformAnimation; 
    return attributes; 
} 

तो में UICollectionViewCell लागू गुण

- (void) applyLayoutAttributes:(AnimationCollectionViewLayoutAttributes *)layoutAttributes 
{ 
    [[self layer] addAnimation:layoutAttributes.transformAnimation forKey:@"transform"]; 
} 
+5

लेआउट क्लास में '[एनीमेशन कोलेक्शन व्यूलाउटएट्रिब्यूट क्लास]' वापस करने के लिए आपको '+ layoutAttributesClass' को ओवरराइड करने की भी आवश्यकता है। –

+1

'+ (कक्षा) लेआउट एट्रिब्यूट्स क्लास { वापसी [एनीमेशन कोलेक्शन व्यूलाउटएट्रिब्यूट क्लास]; } ' –

+1

अपनी CustomFlowLayout.m कक्षा में उपर्युक्त विधि जोड़ें! –

3

UICollectionView आंतरिक रूप से कुछ हार्डकोडेड मूल्य का उपयोग कर सभी एनिमेशन आरंभ करता है। हालांकि, जब तक एनिमेशन प्रतिबद्ध नहीं होते हैं, तब तक आप हमेशा उस मान को ओवरराइड कर सकते हैं। सामान्य में, प्रक्रिया इस प्रकार है:

  • एनिमेशन शुरू
  • सभी लेआउट attribues
  • विचारों के गुण लागू लाने (UICollectionViewCell के)
  • एनिमेशन

विशेषताओं को लागू करने में किया जाता है के लिए प्रतिबद्ध प्रत्येक UICollectionViewCell और आप उपयुक्त विधि में एनीमेशन अवधि ओवरराइड कर सकते हैं। समस्या यह है कि UICollectionViewCell में सार्वजनिक विधि applyLayoutAttributes: है लेकिन यह डिफ़ॉल्ट कार्यान्वयन खाली है! असल में, UICollectionViewCell में _setLayoutAttributes: नामक अन्य निजी विधि है और इस निजी विधि को UICollectionView द्वारा बुलाया जाता है और यह निजी विधि अंत में applyLayoutAttributes: पर कॉल करती है। applyLayoutAttributes: से पहले वर्तमान animationDuration के साथ फ्रेम, स्थिति, परिवर्तन जैसे डिफ़ॉल्ट लेआउट विशेषताओं को लागू किया जाता है। कहा, आप यह जाहिर है, applestore-सुरक्षित नहीं है निजी विधि _setLayoutAttributes:

- (void) _setLayoutAttributes:(PSTCollectionViewLayoutAttributes *)layoutAttributes 
{ 
    [UIView setAnimationDuration:3.0]; 
    [super _setLayoutAttributes:layoutAttributes]; 
} 

में animationDuration ओवरराइड करने के लिए किया है। आप इस निजी विधि को सुरक्षित रूप से ओवरराइड करने के लिए उन रनटाइम हैक्स में से एक का उपयोग कर सकते हैं।

4

सफलता के बिना लेआउट प्रक्रिया के हर संभव चरण में [CATransaction setAnimationDuration:] और [UIView setAnimationDuration:] कोशिश कर के बाद, मैं UICollectionView द्वारा बनाई सेल एनिमेशन कि निजी एपीआई पर निर्भर नहीं करता की अवधि बदलने के लिए कुछ हद तक एक hacky तरीका खोज निकाला।

आप CALayer की speed संपत्ति का उपयोग किसी दिए गए परत पर किए गए एनिमेशन के सापेक्ष मीडिया समय को बदलने के लिए कर सकते हैं। इसके लिए UICollectionView के साथ काम करने के लिए, आप layer.speed को सेल की परत पर 1 से कम कुछ में बदल सकते हैं। स्पष्ट रूप से यह बहुत अच्छा नहीं है कि सेल की परत हमेशा एक गैर-एकता एनीमेशन गति हो, इसलिए एक विकल्प NSNotification को सेल एनीमेशन की तैयारी करते समय प्रेषित करना है, जिस पर आपकी कोशिकाएं सदस्यता लेती हैं, जो परत की गति को बदल देती है, और फिर इसे वापस बदल देती है एनिमेशन समाप्त होने के बाद उचित समय पर।

मैं इस दृष्टिकोण का उपयोग दीर्घकालिक समाधान के रूप में करने की अनुशंसा नहीं करता क्योंकि यह सुंदर चौराहे है, लेकिन यह काम करता है। उम्मीद है कि भविष्य में ऐप्पल UICollectionView एनिमेशन के लिए अधिक विकल्प सामने आएगा।

16

परिवर्तन CALayer की गति

@implementation Cell 
- (id)initWithFrame:(CGRect)frame 
{ 
self = [super initWithFrame:frame]; 
if (self) { 
    self.layer.speed =0.2;//default speed is 1 
} 
return self; 
} 
+4

यह पूरी तरह से काम करता है, कहने में खुशी हुई। यह अन्य कामकाज की तुलना में आसान तरीका है। मैं जोड़ूंगा कि एक उच्च संख्या का मतलब तेज़ एनिमेशन है। – sudo

0

आप बदल सकते हैं UICollectionView संपत्ति layout.speed, कि अपने लेआउट की एनीमेशन अवधि बदलना चाहिए ...

3

आप परत की गति संपत्ति सेट कर सकते हैं (जैसे [रोटोवा के उत्तर]) (https://stackoverflow.com/a/23146861/5271393) नियंत्रण को एनीमेशन की गति को बदलने के लिए। समस्या यह है कि आप मनमानी मानों का उपयोग कर रहे हैं क्योंकि आप सम्मिलन एनीमेशन की वास्तविक अवधि नहीं जानते हैं।

this post का उपयोग करके आप यह पता लगा सकते हैं कि डिफ़ॉल्ट एनीमेशन अवधि क्या है।

newAnimationDuration = (1/layer.speed)*originalAnimationDuration 
layer.speed = originalAnimationDuration/newAnimationDuration 

यदि आप अपने लेआउट तुम में, एनीमेशन 400 मि.से लंबे बनाना चाहते थे:

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UICollectionViewLayoutAttributes* attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:indexPath]; 
    //set attributes here 
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; 
    CGFloat originalAnimationDuration = [CATransaction animationDuration]; 
    CGFloat newAnimationDuration = 0.4f; 
    cell.layer.speed = originalAnimationDuration/newAnimationDuration; 
    return attributes; 
} 

मेरे मामले मैं कोशिकाओं जो बंद स्क्रीन घसीटा जा सकता था और मैं की अवधि को बदलना चाहते हैं पैन इशारा की गति के आधार पर हटाने की एनीमेशन।

इशारा पहचानकर्ता में (जो अपने संग्रह को देखने के हिस्सा होना चाहिए):

- (void)handlePanGesture:(UIPanGestureRecognizer *)sender 
{ 
    CGPoint dragVelocityVector = [sender velocityInView:self.collectionView]; 
    CGFloat dragVelocity = sqrt(dragVelocityVector.x*dragVelocityVector.x + dragVelocityVector.y*dragVelocityVector.y); 
    switch (sender.state) { 
    ... 
    case UIGestureRecognizerStateChanged:{ 
     CustomLayoutClass *layout = (CustomLayoutClass *)self.collectionViewLayout; 
     layout.dragSpeed = fabs(dragVelocity); 
    ... 
    } 
    ... 
} 

फिर अपने customLayout में:

- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UICollectionViewLayoutAttributes* attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:indexPath]; 
    CGFloat animationDistance = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); 
    CGFloat originalAnimationDuration = [CATransaction animationDuration]; 
    CGFloat newAnimationDuration = animationDistance/self.dragSpeed; 
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; 
    cell.layer.speed = originalAnimationDuration/newAnimationDuration; 
    return attributes; 
} 
8

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

[self.collectionView performBatchUpdates:^{ 
    [self.collectionView.viewForBaselineLayout.layer setSpeed:0.2]; 
    [self.collectionView insertItemsAtIndexPaths: insertedIndexPaths]; 
} completion:^(BOOL finished) { 
    [self.collectionView.viewForBaselineLayout.layer setSpeed:1]; 
}]; 
+1

मुझे आश्चर्य है कि बूलियन 'समाप्त' यहां महत्वपूर्ण है या नहीं। कुछ कॉलों पर (मुझे अभी ठीक से याद नहीं है), 'समापन' ब्लॉक को एक से अधिक बार बुलाया जाता है। पूरी तरह से यह सुनिश्चित करने के लिए कि एनिमेशन समाप्त हो गया है, मैं 'if (समाप्त) {/ * ... * /}' करूँगा। यह जरूरी क्यों नहीं है? या यह है और आप बस इसे छोड़ दिया? –

0

@Ashle को अपडेट forBaselineLayout के बाद से yMills

यह काम करता है

self.collectionView.performBatchUpdates({() -> Void in 
     let indexSet = IndexSet(0...(numberOfSections - 1)) 
     self.collectionView.insertSections(indexSet) 
     self.collectionView.forFirstBaselineLayout.layer.speed = 0.5 
} , completion: { (finished) -> Void in 
     self.collectionView.forFirstBaselineLayout.layer.speed = 1.0 
}) 
संबंधित मुद्दे