Is there a way to change the duration of [table beginUpdates]/[table endUpdates] animations?
This is what I've tried, with no luck:
Option 1:
[UIView animateWithDuration:5.0 delay:0.0 options:(UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionOverrideInheritedDuration) animations:^{
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithArray:indexPaths] withRowAnimation:UITableViewRowAnimationTop];
[self.tableView endUpdates];
} completion:^(BOOL finished) {
}];
Option 2:
[CATransaction begin];
[CATransaction setCompletionBlock:^{
NSLog(#"I actually get called!");
}];
[CATransaction setAnimationDuration:5.0]; //but I don't work
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithArray:indexPaths] withRowAnimation:UITableViewRowAnimationTop];
[self.tableView endUpdates];
[CATransaction commit];
Why don't you try UIView animation.
[UIView animateWithDuration:2 delay:0.2 options:UIViewAnimationOptionCurveEaseInEaseOut animations:^{
[self.tableView beginUpdates];
[self.tableView endUpdates];
} completion:^(BOOL finished) {
// code
}];
Here is the Swift version of Gautam Jain's answer 😉:
UIView.animate(withDuration: 2.0, delay: 0.0, options: .curveEaseInOut, animations: {
self.tableView.beginUpdates()
// ...
self.tableView.endUpdates()
}) { isFinished in
// ...
}
#Gautam Jain 's solution is great. However, it has a problem, at least in iOS 9: the completion block will be executed at once but not when the animation completes.
I usually do like below, with a little more code but works better.
[UIView beginAnimations:#"animation" context:nil];
[UIView setAnimationDuration:0.25];
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// completion block
}];
[self.tableView beginUpdates];
// updates
[self.tableView endUpdates];
[CATransaction commit];
[UIView commitAnimations];
Related
How can we change animation transition speed when deleting a row from uitableview, i checked documentation i didn't find any argument where i can specify seconds for transition speed
[self.tableViewMeeting deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationLeft];
Let me know if there is any workaround to this.
Try this:
self.tableView.layer.speed = 0.3
Documentation: https://developer.apple.com/library/prerelease/ios/documentation/GraphicsImaging/Reference/CAMediaTiming_protocol/index.html
[CATransaction begin];
[tableView beginUpdates];
//...
[CATransaction setCompletionBlock: ^{
// Code to be executed upon completion
}];
[tableView insertRowsAtIndexPaths: indexPaths
withRowAnimation: UITableViewRowAnimationAutomatic];
[tableView endUpdates];
[CATransaction commit];
Why don't you try UIView animation.
[UIView animateWithDuration:2
delay:0.2
options:UIViewAnimationOptionCurveEaseInEaseOut
animations:^{
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
completion:^(BOOL finished) {
// code
}
];
i am trying to create a custom animation for the deletion of table rows by overriding the deleteRowsAtIndexPaths method. i created the custom insert animation successfully and tried to reverse the animation for the deletion part but i get some errors: Assertion failure in -[UITableView _endCellAnimationsWithContext:]
and
NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.(rows after update must be equal to rows before the update).
If i don't override the deleteRows method and use tableView's default delete method, everything works fine, but not with my own method.
Here is the overriden method written in my tableViewController class:
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths
withRowAnimation:(UITableViewRowAnimation)animation
{
[[super tableView] insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates]; // Populates UITableView with data
[self.tableView beginUpdates];
for (NSIndexPath *indexPath in indexPaths) {
__block UITableViewCell *cell = [[super tableView] cellForRowAtIndexPath:indexPath];
if (cell) { // If indexPath isn't visible we'll get nil here
// Custom animation
CGRect frame = cell.frame;
frame.origin.x = cell.frame.size.width;
cell.frame = frame;
frame.origin.x = 0;
void (^animationsBlock)() = ^{
cell.frame = frame;
};
if ([[UIView class] respondsToSelector:
#selector(animateWithDuration:delay:usingSpringWithDamping:
initialSpringVelocity:options:animations:completion:)]) {
[UIView animateWithDuration:0.9
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0
options:0
animations:animationsBlock
completion:NULL];
} else {
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:animationsBlock
completion:NULL];
}
}
}
}
deleteRowsAtIndexPathsMethod:
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths
withRowAnimation:(UITableViewRowAnimation)animation
{
[[super tableView] deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates]; // Populates UITableView with data
[self.tableView beginUpdates];
for (NSIndexPath *indexPath in indexPaths) {
__block UITableViewCell *cell = [[super tableView] cellForRowAtIndexPath:indexPath];
if (cell) { // If indexPath isn't visible we'll get nil here
// Custom animation
CGRect frame = cell.frame;
frame.origin.x = 0;
cell.frame = frame;
frame.origin.x = cell.frame.size.width;
void (^animationsBlock)() = ^{
cell.frame = frame;
};
if ([[UIView class] respondsToSelector:
#selector(animateWithDuration:delay:options:animations:completion:)]) {
[UIView animateWithDuration:0.9
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:animationsBlock
completion:NULL];
} else {
[UIView animateWithDuration:0.9
delay:0
options:UIViewAnimationCurveLinear
animations:animationsBlock
completion:NULL];
}
}
}
}
Edit: this code is inside a method, that runs whenever i modify a cell's object. I modify an object and save it to core data, then i retrieve all the updated objects using [self populateData], then i want to run the animations: delete the old cell and insert the new updated cell somewhere at the bottom of the tableView
[coreDataStack saveContext];
[self populateData];
[self.tableView beginUpdates];
int oldIndex = indexPath.row;
int newIndex = newIndexPath.row;
if(oldIndex<newIndex){
[self deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationTop];
} else if (oldIndex>newIndex) {
[self deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationBottom];
} else {
[self.tableView reloadRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}
[self.tableView endUpdates];
I've got a UITableView that allows the user to delete rows in it by swiping them left (very much like the app Clear). My issue is that all the UITableViewCells below the one being deleted animate up to their new frames while the animation for the deleted cell is happening. I want it to happen after the animation for the deleted cell finishes.
My code:
//Starts updating the table
[CATransaction begin];
[CATransaction setAnimationDuration:JTTableViewRowAnimationDuration];
[CATransaction setCompletionBlock: ^{
[self.spenderTable reloadData];
}];
[tableView beginUpdates];
if (state == JTTableViewCellEditingStateLeft)
{
[self.list.spendrItems removeObjectAtIndex:indexPath.row];
//!!Animation happens here!!
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
[tableView endUpdates];
[CATransaction commit];
[tableView performSelector:#selector(reloadVisibleRowsExceptIndexPath:) withObject:indexPath afterDelay:JTTableViewRowAnimationDuration * 2];
Im also using this great UITableView.
Any ideas on how to achieve this?
Got this figured out....for anyone else interested. You can go two ways with:
Approach #1 (Wrap in animation block):
[UIView animateWithDuration:0.25 animations:^{
[[(JTTransformableTableViewCell *)[self.spenderTable cellForRowAtIndexPath:indexPath] contentView] setX:-320];
}completion:^(BOOL done){
[self.list.spendrItems removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
Remember to manipulate the datasource in the completion block to avoid an internal consistency exception.
Approach #2 (inherit from UITableView):
Override the animation like so:
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation{
for (NSIndexPath *indexPath in indexPaths)
{
UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath];
[cell setFrame:CGRectMake(320, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
[UIView beginAnimations:NULL context:nil];
[UIView setAnimationDuration:1];
[cell setFrame:CGRectMake(0, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
[UIView commitAnimations];
}
}
I'm switching between layouts in a uicollectionview using this code
-(void)setHorizontalLayout:(BOOL)layout
{
if (layout == YES)
{
[self.collectionView performBatchUpdates:^{
[self.collectionView setCollectionViewLayout:self.horizontalPagingLayout animated:NO];
[self.collectionView setPagingEnabled:YES];
self.galleryInHorizontalScrollMode = YES;
} completion:^(BOOL finished) {
[self.collectionView reloadData];
}];
}
else {
[self.collectionView performBatchUpdates:^{
[self.collectionView setCollectionViewLayout:self.galleryLayout animated:NO];
self.galleryInHorizontalScrollMode = NO;
[self.collectionView setPagingEnabled:NO];
} completion:^(BOOL finished) {
[self.collectionView reloadData];
}];
}
return;
}
but after I change from one layout to the other I am getting the [0,0] indexpathed cell set as the uicollectionview's background. I suspect something with the layer is messed up. And looking over the call stack all looks fine. I can't see any calls to animations that may be messing with the layer.
Any way to invalidate this? what could cause it?
I'm not sure on why this behavior happens but when I invalidated the layout before changing the layout all works as expected:
[self.collectionView performBatchUpdates:^{
***[self.collectionView.collectionViewLayout invalidateLayout];***
[self.collectionView setCollectionViewLayout:self.horizontalPagingLayout animated:NO];
} completion:^(BOOL finished) {
[self.collectionView reloadData];
}];
How can I achieve the animation where a UICollectionViewCell with flip and grow to show modal view on tap?
Here is what I used in another project and it was working well :
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
if (cell.selected) {
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
[UIView transitionWithView:cell
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[cell setFrame:self.selectedCellDefaultFrame];
cell.transform = self.selectedCellDefaultTransform;
}
completion:^(BOOL finished) {
self.selectedCellDefaultFrame = CGRectZero;
[collectionView reloadItemsAtIndexPaths:#[indexPath]];
}];
return NO;
}
else {
return YES;
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
[cell.superview bringSubviewToFront:cell];
self.selectedCellDefaultFrame = cell.frame;
self.selectedCellDefaultTransform = cell.transform;
[UIView transitionWithView:cell
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[cell setFrame:collectionView.bounds];
cell.transform = CGAffineTransformMakeRotation(0.0);
}
completion:^(BOOL finished) {}];
}
Different things here :
The bringSubviewToFront: message call is used to prevent the cell to animate behind the other cells
We use two properties declared in the controller : selectedCellDefaultFrameand selectedCellDefaultTransform to save the default state of the cell and reinitialize it when deselecting
When deselecting, we call the reloadItemsAtIndexPaths: method of UICollectionView to be sure that the reset of the position is totally complete
Let me know if you have any trouble with this.
Good luck,
I haven't tried the grow animation, but I think I can help with the UICollectionViewCell flip animation.
Try:
UICollectionViewCell* cell = [collectionView cellForItemAtIndexPath:indexPath];
[UIView animateWithDuration:1.0
delay:0
options:(UIViewAnimationOptionAllowUserInteraction)
animations:^
{
NSLog(#"starting animation");
[UIView transitionFromView:cell.contentView
toView:newView
duration:.5
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
completion:^(BOOL finished)
{
NSLog(#"animation end");
}
];
Hope that helps!