11

I want to do the animation introduced by Android Material Design Hierarchical Timing in iOS using UICollectionView

Lets say its a collectionView, and resizing the view is not an issue, what would be the best practice to do this animation in that timely fashion. How to perform the delay

Raheel Sadiq
  • 9,847
  • 6
  • 42
  • 54

3 Answers3

4

Subclass your collection view cell and add this method:

class YourCollectionViewCell: UICollectionViewCell {  
   //Some IBOutlets if needed

   func popAfter(delay: NSTimeInterval){
      transform = CGAffineTransformMakeScale(0, 0)
      UIView.animateWithDuration(0.7, delay: delay, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
         self.transform = CGAffineTransformMakeScale(1, 1)
      }, completion: nil)      
   }
}

Set your collection view's delegate and dataSource to your view controller (this can be done in Interface Builder). Add constants to your view controller and reload collection view in viewDidAppear to animate cells:

class YourViewController{
   private let numberOfCellsInLine = 3
   private let numberOfVisibleCellsOnTheScreen = 12
   private let baseDelay = 0.15

   override func viewDidAppear(animated: Bool) {
      super.viewDidAppear(animated)
      collectionView.reloadData()
   }
}

Add extension to your view controller for UICollectionView datasource & delegate:

extension YourViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
   func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
      return numberOfCells
   }

   // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
   func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
      let cell = collectionView.dequeueReusableCellWithReuseIdentifier("YourCellID", forIndexPath: indexPath) as YourCollectionViewCell
      //set cell's content
      let index = indexPath.row
      let yIndex = index / numberOfCellsInLine
      let delay = Double(index % numberOfCellsInLine + (index >= numberOfVisibleCellsOnTheScreen ? 0 : yIndex)) * baseDelay
      cell.popAfter(delay)
      return cell
   }
}

You can adjust baseDelay and animation duration in Cell's popAfter values to achieve desired timing. Hope this helps and good luck with your app! Feel free to ask any questions.

Nikita Arkhipov
  • 1,208
  • 4
  • 11
  • 28
2

One way to do this would be to add the cells one at a time with a timer, and have those cells expand to full size as they come on to the window.

#import "ViewController.h"
#import "RDCollectionViewCell.h"

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
@property (weak,nonatomic) IBOutlet UICollectionView *collectionview;
@property (strong,nonatomic) NSMutableArray *mutableArray;
@property (strong,nonatomic) NSArray *data;
@end

@implementation ViewController

-(void)viewDidLoad {
    [super viewDidLoad];
    self.mutableArray = [NSMutableArray new];
    self.data = @[@"one", @"two", @"three", @"four", @"five", @"six", @"seven", @"eight", @"nine", @"ten"];
    [self performSelector:@selector(startTimer) withObject:nil afterDelay:0.5];
}

-(void)startTimer {
    [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:@selector(addCells:) userInfo:nil repeats:YES];
}


-(void)addCells:(NSTimer *) timer {
    static int counter = 0;
    [self.mutableArray addObject:self.data[counter]];
    counter ++;
    [self.collectionview insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:self.mutableArray.count -1 inSection:0]]];
    if (self.mutableArray.count == self.data.count) {
        [timer invalidate];
    }
}


-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.mutableArray.count;
}


-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    RDCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.contentView.backgroundColor = (indexPath.row % 2 == 0)? [UIColor colorWithRed:180/255.0 green:210/255.0 blue:254/255.0 alpha:1] : [UIColor colorWithRed:50/255.0 green:167/255.0 blue:85/255.0 alpha:1];
    cell.label.text = self.mutableArray[indexPath.row];
    return cell;
}

In the custom cell class,

@implementation RDCollectionViewCell

-(void)awakeFromNib {
    self.contentView.transform = CGAffineTransformMakeScale(0.01, 0.01);
}


-(void)didMoveToWindow {
    [UIView animateWithDuration:0.3 delay:0.1 options:0 animations:^{
        self.contentView.transform = CGAffineTransformIdentity;
    } completion: nil];
}

The project can be found here, http://jmp.sh/aDw846R

rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • not working on this animation, design and animation changed later, but interesting answer, saved it – Raheel Sadiq Apr 05 '15 at 10:22
  • 1
    Actually, @rdelmar's answer is correct as far as the question "What would be the best practice to do this animation in that timely fashion ?" is concerned, but since your opening statement were, "I want to do the animation introduced by Android Material Design Hierarchical Timing in iOS using UICollectionView" the answer need certain changes. You see, Hierarchical Timing Animation happens diagonally, starts from Top-Left corner, ends at Bottom-Right corner, so to achieve this we can't use FlowLayout, FlowLayout adds cells left-to-right and top-to-bottom. – Amit Singh Apr 08 '15 at 08:00
  • Thanks for your answer @rdelmar. Awarding the bounty now. If you have a chance I'd love to see your thoughts on Amit's comment. – Aaron Brager Apr 09 '15 at 18:11
  • @AaronBrager Amit is correct. There are some differences in the 2 animations which I couldn't see easily until I recorded them and watched frame by frame. The Android animation does go in a more diagonal fashion; the bottom left square starts to appear after the second one on the top row, whereas in my animation, the bottom left square doesn't appear until the 5th one on the top row starts to grow (but is still small). I think he's wrong about not being able to use the flow layout though. I'll work on this a little more to see if I can more closely mimic the Android animation. – rdelmar Apr 09 '15 at 22:56
  • 1
    @AaronBrager After looking into this a little more, what I would have to do to make it more like the Android animation, made my code similar to Nikita's. I tried his code, and it was nearly identical to the Android animation. His answer should be accepted as the correct one. – rdelmar Apr 09 '15 at 23:54
0

Something like this should work. Random pieces thrown together, I know.

Here's a very well done pen of what you're describing. In it you'll find that the timing function in question is the curve, defined in CSS as:

cubic-bezier(0.650, 0.045, 0.405, 1.000)

Knowing the timing function, you can implement this animation in iOS. Here's a link to another question -- it shows how to pull off custom animations and is very well explained.

Custom timing functions in iOS

May take some simple math, but this should help! ...i hope.

cheers

Community
  • 1
  • 1
Todd
  • 5,314
  • 3
  • 28
  • 45