UICollectionViewCell Flip and Move
In my previous post I asked how to both flip and move a UIView. My co-worker Jason Hanson found that adding a “container” view and moving that while flipping the child view worked great. So the example code now looks like this:
[UIViewtransitionWithView:self.flipView
duration:5.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
self.containerView.frame = CGRectOffset(self.containerView.frame, 0, –250);
self.flipView.backgroundColor = [UIColor redColor];
} completion:^(BOOL finished) {
self.isFlipViewUp = NO;
}];
With that solved, I could move on to implementing the bigger picture effect I was working on – flipping a UICollectionViewCell, centering it in the window with a modal type background, then flipping it back.
I love this effect, and I’m proud of my code. But I’m wondering if there is a better way to do it.
To demonstrate I created a little sample project that flips a playing card:
The initial flip needs to:
1. Flip the card from its back to its face
2. Add a gray overlay to the rest of the visible screen
3. Center the card on the screen
Here’s the code:
– (void)flipCard
{
// grap the screen size and create a view with the same frame
// set its background to clear so it can be animated
UIWindow *mainWindow = [[[UIApplicationsharedApplication] delegate] window];
self.modalView = [[UIView alloc] initWithFrame:mainWindow.frame];
self.modalView.backgroundColor = [UIColorclearColor];
[mainWindow addSubview:self.modalView];
// remove the tapToPresentCardGesture
// then add the tapToHideCardGesture both to the cell and the modal view
[self.contentView removeGestureRecognizer:self.tapToPresentCardGesture];
[self.contentView addGestureRecognizer:self.tapToHideCardGesture];
[self.modalView addGestureRecognizer:self.tapToHideCardGesture];
// translate the frame of the cell to the main window
self.originalFrame = self.frame;
CGPoint translatedOrigin = [self convertPoint:self.bounds.origin toView:self.modalView];
self.translatedFrame = CGRectMake(translatedOrigin.x, translatedOrigin.y, self.frame.size.width, self.frame.size.height);
self.frame = self.translatedFrame;
// add the cell to the modalView
[self.modalView addSubview:self];
// flip the contentView while repositioning the actual cell view
[UIViewtransitionWithView:self.contentView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
// animate the backgroundColor of the modalView to a semi-transparent black
self.modalView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.75];
// move the cell to the center of the screen
self.center = mainWindow.center;
// change the imageView to the card image
[self.cardImageView setImage:[UIImage imageNamed:@”AC”]];
} completion:nil];
}
When it flips back it needs to:
1. Flip the card from its face to its back
2. Remove the gray overlay
3. Replace the card back into the UICollectionView
Here’s that code:
– (void)hideCard
{
// flip the contentView back from right to left
[UIViewtransitionWithView:self.contentView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
// animate the backgroundColor of the modalView to clear
self.modalView.backgroundColor = [UIColor clearColor];
// animate the cell back to its original spot
self.frame = self.translatedFrame;
// set its image back to “Back”
[self.cardImageView setImage:[UIImage imageNamed:@”Back”]];
} completion:^(BOOL finished) {
// remove the modalView along with its tapGesture and set it to nil
[self.modalView removeFromSuperview];
[self.modalView removeGestureRecognizer:self.tapToHideCardGesture];
self.modalView = nil;
// reset the tap on the contentView to show the card
[self.contentView removeGestureRecognizer:self.tapToHideCardGesture];
[self.contentView addGestureRecognizer:self.tapToPresentCardGesture];
// tell the delegate that the cell is back
[self.delegate cardDidHide];
}];
}
Notice that it calls a delegate. The delegate is the UICollectionView which simply calls reloadData to get the cell back:
– (void)cardDidHide
{
// when the cell comes back it needs to be added back to this view
// calling reloadData will do this correctly
[self.collectionView reloadData];
};
Is this the simplest way of doing this? Could I do this better?
I encountered this behaviour as well, and I’m wondering if you knew the reason behind it…
It seems weird that half of the animation gets clipped unless you add it to another view
mccc
August 16, 2014 at 1:46 pm
Hi Nick,
was trying to port your code to swift. The code seems to be removed from bitbucket.
Any chance it’s coming back there ?
Best regards and keep op the good work !
Joris Eversen (@JorisEversen)
September 9, 2015 at 6:07 am