问题
Does the model change and the row animation have to be started directly from commitEditingStyle
or can it be done in a later run loop iteration?
I am asking because it appears to work on iOS 10, but on iOS 11 it breaks at least the delete animation. Is it simply a bug in iOS 11 or is it a bad idea in general?
Is there a better way to trigger an asynchronous delete operation and animate the table view change on completion?
The first picture shows how it breaks on iOS 11 (The delete button overlaps the next cell). The second picture shows how it looks fine on iOS 10.
This is the interesting snipped:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
dispatch_async(dispatch_get_main_queue(), ^{
[_model removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
});
}
}
If I remove the dispatch_async(...
, it works as expected on iOS 10 and 11. The first picture shows iOS 11, the second iOS 10.
Here is the full code of the table view controller used to test it:
#import "TableViewController.h"
@implementation TableViewController {
NSMutableArray<NSString *>* _model;
}
- (void)viewDidLoad {
[super viewDidLoad];
_model = [[NSMutableArray alloc] init];
[self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"cell"];
for (NSInteger i = 0; i < 200; i++) {
[_model addObject:[NSString stringWithFormat:@"Test Row %ld Test Test Test Test Test", (long)i]];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _model.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
cell.textLabel.text = _model[indexPath.row];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
dispatch_async(dispatch_get_main_queue(), ^{
[_model removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
});
}
}
@end
Update:
Adding this method to the table view controller fixes it for iOS 11 and allows delaying the model change and row animation. (Thanks to ChrisHaze)
- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(11_0) {
UIContextualAction* deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"Delete" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
dispatch_async(dispatch_get_main_queue(), ^{
[_model removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
completionHandler(YES);
});
}];
UISwipeActionsConfiguration* config = [UISwipeActionsConfiguration configurationWithActions:@[deleteAction]];
return config;
}
回答1:
It seems that there have been changes made to the UITableview in iOS 11.
Apple's Build release notes :
"The behavior of the delete swipe action has been changed.When implementing commitEditingStyle: to delete a swiped row, delete the row in the data source and call deleteRowsAtIndexPaths: on the table view to show the swipe delete animation."
After further research, I found that you must call the beginUpdates
prior to calling the deleteRowAtIndexPath:
method, along with the endUpdates
method after.
According to Apple's documentation, not calling these tableView methods will result in potential data model issues and effect the deletion animations.
With that said, there is an answer that includes the required code to a question on the Apple dev forums that addresses a very similar question.
Answering your actual questions:
- Model changes need to be called prior to the beginUpdates and endUpdates block, in which the UI changes will occur.
- Updating the UITableView will alleviate the synchronization/animation issues.
---------------------------- additional details ----------------------------
After looking into the details within your comment, I've included a link (above) that is the latest Table View Programming Guide provided by Apple. It will take you, or anyone else with this issue, to the section of the guide that includes the details you've added to your question.
There is also a handy note that includes how to make the deleteRowAtIndexPath
call from within the commitEditingStyle
method if one must.
来源:https://stackoverflow.com/questions/47147342/delaying-the-model-and-table-view-change-in-commiteditingstyle