How to detect that animation has ended on UITableView beginUpdates/endUpdates?
IphoneObjective CIosIpadIphone Problem Overview
I am inserting/deleting table cell using insertRowsAtIndexPaths/deleteRowsAtIndexPaths
wrapped in beginUpdates/endUpdates
. I am also using beginUpdates/endUpdates
when adjusting rowHeight. All these operations are animated by default.
How can I detect that animation has ended when using beginUpdates/endUpdates
?
Iphone Solutions
Solution 1 - Iphone
What about this?
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// animation has finished
}];
[tableView beginUpdates];
// do some work
[tableView endUpdates];
[CATransaction commit];
This works because the tableView animations use CALayer
animations internally. That is, they add the animations to any open CATransaction
. If no open CATransaction
exists (the normal case), then one is implicitly began, which is ended at the end of the current runloop. But if you begin one yourself, like is done here, then it will use that one.
Solution 2 - Iphone
Swift Version
CATransaction.begin()
CATransaction.setCompletionBlock({
do.something()
})
tableView.beginUpdates()
tableView.endUpdates()
CATransaction.commit()
Solution 3 - Iphone
If you're targeting iOS 11 and above, you should use UITableView.performBatchUpdates(_:completion:)
instead:
tableView.performBatchUpdates({
// delete some cells
// insert some cells
}, completion: { finished in
// animation complete
})
Solution 4 - Iphone
A possible solution could be to inherit from the UITableView on which you call endUpdates
and overwrite its setContentSizeMethod
, since UITableView adjusts its content size to match the added or removed rows. This approach should also work for reloadData
.
To ensure that a notification is sent only after endUpdates
is called, one could also overwrite endUpdates
and set a flag there.
// somewhere in header
@private BOOL endUpdatesWasCalled_;
-------------------
// in implementation file
- (void)endUpdates {
[super endUpdates];
endUpdatesWasCalled_ = YES;
}
- (void)setContentSize:(CGSize)contentSize {
[super setContentSize:contentSize];
if (endUpdatesWasCalled_) {
[self notifyEndUpdatesFinished];
endUpdatesWasCalled_ = NO;
}
}
Solution 5 - Iphone
You can enclose your operation(s) in UIView animation block like so:
- (void)tableView:(UITableView *)tableView performOperation:(void(^)())operation completion:(void(^)(BOOL finished))completion
{
[UIView animateWithDuration:0.0 animations:^{ [tableView beginUpdates];
if (operation)
operation();
[tableView endUpdates];
} completion:^(BOOL finished) {
if (completion)
completion(finished);
}];
}
Credits to https://stackoverflow.com/a/12905114/634940.
Solution 6 - Iphone
Haven't found a good solution yet (short of subclassing UITableView). I've decided to use performSelector:withObject:afterDelay:
for now. Not ideal, but gets the job done.
UPDATE: It looks like I can use scrollViewDidEndScrollingAnimation:
for this purpose (this is specific to my implementation, see comment).
Solution 7 - Iphone
You can use tableView:willDisplayCell:forRowAtIndexPath:
like:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"tableView willDisplay Cell");
cell.backgroundColor = [UIColor colorWithWhite:((indexPath.row % 2) ? 0.25 : 0) alpha:0.70];
}
But this will also get called when a cell that is already in the table moves from off the screen to on the screen so it may not be exactly what you are looking for. I just looked through all the UITableView
and UIScrollView
delegate methods and there doesnt appear to be anything to handle just after a cell is inserted animation.
Why not just call the method you want to be called when the animation ends after the endUpdates
?
- (void)setDownloadedImage:(NSMutableDictionary *)d {
NSIndexPath *indexPath = (NSIndexPath *)[d objectForKey:@"IndexPath"];
[indexPathDelayed addObject:indexPath];
if (!([table isDragging] || [table isDecelerating])) {
[table beginUpdates];
[table insertRowsAtIndexPaths:indexPathDelayed withRowAnimation:UITableViewRowAnimationFade];
[table endUpdates];
// --> Call Method Here <--
loadingView.hidden = YES;
[indexPathDelayed removeAllObjects];
}
}