I was trying to achieve similar to google+ animation on showing new UITableViewCells when they appear for the first time.
This is what I'm doing:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([[self serverFetchControllerForTableView:tableView] hasDataAtIndexPath:indexPath])
{
if (![[self shownIndexesForTableView:tableView] containsObject:indexPath]) {
[[self shownIndexesForTableView:tableView] addObject:indexPath];
UIView* view = [cell contentView];
view.layer.transform = self.initialTransformation;
view.layer.opacity = 0.0;
[UIView animateWithDuration:ANIMATION_CELL_APPEARANCE_TIME animations:^{
view.layer.transform = CATransform3DIdentity;
view.layer.opacity = 1;
}];
}
}
}
//initial transformation looks like that
CGFloat rotationAngleRadians = ANIMATION_CELL_APPEARANCE_ANGLE_DEG * (M_PI/180);
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DRotate(transform, rotationAngleRadians, 1.0, 0.0, 1.0);
transform = CATransform3DTranslate(transform, ANIMATION_CELL_APPEARANCE_X, ANIMATION_CELL_APPEARANCE_Y, 0.0);
self.initialTransformation = transform;
Everything works in terms of visuals, but while those cells are appearing, I'm losing complete control of UITableView scrolling - I cannot touch cells, stop scrolling or select any of them, until animation is finished.
Does anyone have any suggestion what I can do better here to fix that problem ?
Same problem can be found here:
http://www.raywenderlich.com/49311/advanced-table-view-animations-tutorial-drop-in-cards
Try putting time to 3.0seconds in TipInCellAnimator any for 3.0 control is lost completely.
This is the normal behavior for animations using one of the animateWithDuration... methods. If you want user interaction during the animation, try using
animateWithDuration:delay:options:animations:completion: and passing UIViewAnimationOptionAllowUserInteraction as the option. I don't know what that might do to your animation, but it should allow user interaction.
Related
Here is my willDisplayCell animation code I want to animate only once, don't repeat when scroll up the tableview.
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
UIView *cellContentView = [cell contentView];
CGFloat rotationAngleDegrees = -30;
CGFloat rotationAngleRadians = rotationAngleDegrees * (M_PI/180);
CGPoint offsetPositioning = CGPointMake(0, cell.contentView.frame.size.height*4);
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DRotate(transform, rotationAngleRadians, -50.0, 0.0, 1.0);
transform = CATransform3DTranslate(transform, offsetPositioning.x, offsetPositioning.y, -50.0);
cellContentView.layer.transform = transform;
cellContentView.layer.opacity = 0.8;
[UIView animateWithDuration:0.95 delay:00 usingSpringWithDamping:0.85 initialSpringVelocity:0.8 options:0 animations:^{
cellContentView.layer.transform = CATransform3DIdentity;
cellContentView.layer.opacity = 1;
} completion:^(BOOL finished) {}];
}
If you want any of your code to run only once, you can use dipatch_once. This will make sure that this code is run only once in life span of parent object. You can put that code inside dispatch_async if that code is being run from somewhere other than tableview's event handlers.
dispatch_async(dispatch_get_main_queue(),^{
static dispatch_once_t once;
dispatch_once(&once, ^{
//Your animation code.
});
};
In order to animate each cell only once each cell has to know that it animated itself and it shouldn't again. Easiest way to achieve that is to give the cell BOOL variable called animated to determine whether if or not the cell has already animated.
Create custom cell class
Create new Cocoa Class called for example CustomCell. It should subclass the UITableViewCell.
Inside CustomCell.h declare a BOOL property called animated:
#property (nonatomic) BOOL animated;
If you are using storyboards like me, remember to also update the class of a cell in storyboard editor, just like you do when you are creating new view controllers
That's it here.
Modify View Controller
Now you should import your new class and swap (UITableViewCell *) with (CustomCell *) in your tableViews delegate methods.
In willDisplayCell: add necessary logic, it will look like this:
-(void)tableView:(UITableView *)tableView willDisplayCell:(CustomCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if(!cell.animated){ //added
cell.animated = YES; //added
UIView *cellContentView = [cell contentView];
CGFloat rotationAngleDegrees = -30;
CGFloat rotationAngleRadians = rotationAngleDegrees * (M_PI/180);
CGPoint offsetPositioning = CGPointMake(0, cell.contentView.frame.size.height*4);
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DRotate(transform, rotationAngleRadians, -50.0, 0.0, 1.0);
transform = CATransform3DTranslate(transform, offsetPositioning.x, offsetPositioning.y, -50.0);
cellContentView.layer.transform = transform;
cellContentView.layer.opacity = 0.8;
[UIView animateWithDuration:0.95 delay:00 usingSpringWithDamping:0.85 initialSpringVelocity:0.8 options:0 animations:^{
cellContentView.layer.transform = CATransform3DIdentity;
cellContentView.layer.opacity = 1;
} completion:^(BOOL finished) {}];
}
And this is it. Hope it works for you
I wants to do animation something similar to app launch on the ios home screen. Like the whole collection view goes to scale up and the launched app covered the whole screen.
I am using the iOS 7 new api for viewcontroller transitions.
And I am using the parent collection viewcontroller snapshot to appropriate animation.
But still I am not getting enough like what animation is actually is happening at that time?
To get the performance and look you want, you may have to perform transforms on the view layers.
I put up a little demo on GitHub, but the relevant code is below.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCellView *cell = (MyCellView *)[self collectionView:collectionView cellForItemAtIndexPath:indexPath];
self.detailViewController = [[DetailViewController alloc] initWithNibName:nil bundle:nil];
self.detailViewController.labelString = [NSString stringWithFormat:#"%i", indexPath.row];
[self.view.superview addSubview:self.detailViewController.view];
// tap position relative to collection view
float screenX = self.collectionView.frame.origin.x + cell.center.x;
float screenY = self.collectionView.frame.origin.y + cell.center.y - self.collectionView.contentOffset.y;
// tap position relative to view frame
float translateX = (self.view.frame.size.width / -2.0) + screenX;
float translateY = (self.view.frame.size.height / -2.0) + screenY;
CATransform3D transform_detail = CATransform3DScale(CATransform3DMakeTranslation(translateX, translateY, 0.0), 0.0, 0.0, 0.0);
CATransform3D transform_main = CATransform3DScale(CATransform3DMakeTranslation(-translateX * 5.0, -translateY * 5.0, 0.0), 5.0, 5.0, 5.0);
self.detailViewController.view.layer.transform = transform_detail;
[UIView animateWithDuration:0.5 animations:^{
self.detailViewController.view.layer.transform = CATransform3DIdentity;
self.view.layer.transform = transform_main;
} completion:^(BOOL finished) {
self.view.layer.transform = CATransform3DIdentity;
}];
}
You need to use custom segues.
This tutorial should help you and below is the code excerpt.
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
// Add the destination view as a subview, temporarily
[sourceViewController.view addSubview:destinationViewController.view];
// Transformation start scale
destinationViewController.view.transform = CGAffineTransformMakeScale(0.05, 0.05);
// Store original centre point of the destination view
CGPoint originalCenter = destinationViewController.view.center;
// Set center to start point of the button
destinationViewController.view.center = self.originatingPoint;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
// Grow!
destinationViewController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
destinationViewController.view.center = originalCenter;
}
completion:^(BOOL finished){
[destinationViewController.view removeFromSuperview]; // remove from temp super view
[sourceViewController presentViewController:destinationViewController animated:NO completion:NULL]; // present VC
}];
}
I have an animation going on in -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *). The cells essentially rotate in (code below). They do this every time they load onto the screen. The cells that rotate in initially (that is, the ones visible on screen at the beginning) look like the "Before" snapshot below. Small (normal) indentation. After scrolling down or around, new cells are animated onto the screen, but they look like the "After" shot. They're off by a few pixels. Once I scroll up again, the upper cells are reloaded and also have the larger indentation. All of which would be fine, except depending on scroll behavior, sometimes some cells are indented and others aren't and it looks awful. I tried resetting the indent property of UITableViewCell but it didn't do anything. Any ideas what could cause this odd behavior? Perhaps a property I'm not aware of I should be setting? Thanks!
Before:
After:
//Animation Code Borrowed From http://www.thinkandbuild.it/animating-uitableview-cells/
//1. Setup the CATransform3D structure
CATransform3D rotation;
rotation = CATransform3DMakeRotation( (90.0*M_PI)/180, 0.0, 0.7, 0.4);
rotation.m34 = 1.0/ -600;
//2. Define the initial state (Before the animation)
cell.layer.shadowColor = [[UIColor blackColor]CGColor];
cell.layer.shadowOffset = CGSizeMake(10, 10);
cell.alpha = 0;
cell.layer.transform = rotation;
cell.layer.anchorPoint = CGPointMake(0, 0.5);
//3. Define the final state (After the animation) and commit the animation
[UIView beginAnimations:#"rotation" context:NULL];
[UIView setAnimationDuration:0.8];
cell.layer.transform = CATransform3DIdentity;
cell.alpha = 1;
cell.layer.shadowOffset = CGSizeMake(0, 0);
[UIView commitAnimations];
EDIT
After going to the original site's source code again, I noticed an update to the code and I applied it:
if(cell.layer.position.x != 0){
cell.layer.position = CGPointMake(0, cell.layer.position.y);
}
However, it didn't seem to fix this issue, and I think it was a fix for another positioning issue this animation had.
i am not sure just give it a try,
just by forcing the layer position to origin, see below code
//1. Setup the CATransform3D structure
CATransform3D rotation;
rotation = CATransform3DMakeRotation( (90.0*M_PI)/180, 0.0, 0.7, 0.4);
rotation.m34 = 1.0/ -600;
//2. Define the initial state (Before the animation)
cell.layer.shadowColor = [[UIColor blackColor]CGColor];
cell.layer.shadowOffset = CGSizeMake(10, 10);
cell.alpha = 0;
cell.layer.transform = rotation;
cell.layer.anchorPoint = CGPointMake(0, 0.5);
if(cell.layer.position.x != 0)
{
cell.layer.position = CGPointMake(0, cell.layer.position.y);
}
//add this and try
CGPoint point = cell.layer.position;
point.x = 0.0f; //setting the position back to original (for your case)
cell.layer.position = point;
//4. Define the final state (After the animation) and commit the animation
[UIView beginAnimations:#"rotation" context:NULL];
[UIView setAnimationDuration:0.8];
cell.layer.transform = CATransform3DIdentity;
cell.alpha = 1;
cell.layer.shadowOffset = CGSizeMake(0, 0);
[UIView commitAnimations];
In a UITableViewController, this function logs into the console the x coordinate of the tableview cells
- (void)tableView:(UITableView *)tableView
willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
//1. Setup the CATransform3D structure
CATransform3D rotation;
rotation = CATransform3DMakeRotation( (45.0*M_PI)/180, 0.0, 0.7, 0.4);
rotation.m34 = 1.0/ -600;
//2. Define the initial state (Before the animation)
cell.layer.shadowColor = [[UIColor blackColor]CGColor];
cell.layer.shadowOffset = CGSizeMake(10, 10);
cell.alpha = 0;
cell.layer.transform = rotation;
cell.layer.anchorPoint = CGPointMake(0, 0.5);
//4. Define the final state (After the animation) and commit the animation
[UIView beginAnimations:#"rotation" context:NULL];
[UIView setAnimationDuration:0.3];
cell.layer.transform = CATransform3DIdentity;
cell.alpha = 1;
cell.layer.shadowOffset = CGSizeMake(0, 0);
[UIView commitAnimations];
NSLog(#"X%d= %d,Y%d=%d",(int)indexPath.row ,(int)cell.layer.position.x,(int)indexPath.row,(int)cell.layer.position.y);
}
it logs for the cells that are initially displayed (cell 0 to 12)
X0= 160,Y0=25
X1= 160,Y1=75
X2= 160,Y2=125
X3= 160,Y3=175
X4= 160,Y4=225
X5= 160,Y5=275
X6= 160,Y6=325
X7= 160,Y7=375
X8= 160,Y8=425
X9= 160,Y9=475
X10= 160,Y10=525
X11= 160,Y11=575
X12= 160,Y12=625
But after scrolling the tableview, it logs
X13= 0,Y13=675
X14= 0,Y14=725
X15= 0,Y15=775
X16= 0,Y16=825
and only cell 13's view is offset by 160 in the simulator
This could be fixed by adding to the previous method
if(cell.layer.position.x != 0){
cell.layer.position = CGPointMake(0, cell.layer.position.y);
}
What I am asking is why initially the x coordinate = 160 ???
The position of a layer denotes its center position, and not the top-left corner. you changed that behaviour through manipulation of the anchorPoint property. Insert the following line after the line that contains anchorPoint:
cell.layer.position = CGPointMake(0, cell.layer.position.y);
Changing the anchorPoint also changes the frame. To keep the frame the same, you have to do the opposite movement with the position property.
(To get the position of the top-left corner, you would use cell.frame.origin instead.)
See also: CALayer reference
I am trying to create UITableCellView animation like Peek Calendar. As can be seen here:
I am using UITableView and the following code for animation
//Animaiton for the table view cells to appear
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if([cell.reuseIdentifier isEqualToString:#"HolidayCell"]) {
int x = (indexPath.row-self.selectedMonth) %2;
//1. Setup the CATransform3D structure
CATransform3D rotation;
CATransform3D position;
position = CATransform3DMakeTranslation(0, -36, 0) ;
//rotation.m33 = 12;
UILabel *shadow = (UILabel*)[cell.contentView viewWithTag:2];
//2. Define the initial state (Before the animation)
cell.alpha = 1;
shadow.alpha = 0.75;
if(x==1) {
rotation = CATransform3DMakeRotation((-90.0*M_PI)/180, 1.5, 0.0, 0.0);
rotation.m34 = 0.0059;
cell.layer.anchorPoint = CGPointMake(0.5, 0.0);
}
else
{
rotation = CATransform3DMakeRotation((90.0*M_PI)/180, 1.5, 0.0, 0.0);
rotation.m34 = 0.0059;
cell.layer.anchorPoint = CGPointMake(0.5, 1.0);
}
cell.layer.transform = CATransform3DConcat(position, rotation);
//3. Define the final state (After the animation) and commit the animation
[UIView beginAnimations:#"rotation" context:NULL];
[UIView setAnimationDuration:2.3];
//[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:cell cache:YES];
cell.layer.transform = CATransform3DIdentity;
cell.alpha = 1;
shadow.alpha = 0;
//cell.layer.shadowOff
1. List item
set = CGSizeMake(0, 0);
[UIView commitAnimations];
}
}
Now I don't have any issue in particular with the animation. The cells are rotating just fine. However my issue is that when a cell i inserted even while rotated it is still taking up space as if it were fully open.
As you can see the cells are rotating within their "virtual boxes" however I want the cells to stick together as depicted in this image:
Does anyone have any idea on how to do this?
You are not transforming the frame, only the layer. The layer is only the visual representation of the cell, and does not define size or position of the cell.
I believe, in a tableView, the frame of a cell is defined first by the heightForRowAtIndexPath: method, but you should be able to change and animate it later using the "frame" property. Try setting it to zero, and animate to the final height when the cell appears.
Code, that finally gave the solution: https://www.cocoacontrols.com/controls/jtgesturebasedtableviewdemo
Here is some code, that actually works (tested it).
Put it into the
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath method, and comment out the other code for testing
CGRect cellFrame = cell.frame;
//replace with the y coordinate of the point, where the cell(s) get inserted
float insertPoint = 0;
//replace with the duration you wish
float animationDuration = 5;
[cell setFrame:CGRectMake(cellFrame.origin.x, insertPoint, cellFrame.size.width, 0)];
[UIView animateWithDuration:animationDuration animations:^{
[cell setFrame:cellFrame];
}];
That may not be exactly what you want, but it should give you an idea...