背景
公司项目有一个消息的列表页,点击自定义cell上的删除按钮执行删除操作
#import "MyCell.h"
、、、、、
-(void)rowDelete:(UIButton *)btn{
if (self.delegate && [self.delegate respondsToSelector:@selector(deleteNewsBtnClicked:)]) {
[self.delegate deleteNewsBtnClicked:self.buttonTag];
}
}
#import "ViewController.h"
、、、、、、
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (!cell) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
cell.delegate = self;
}
cell.titleLab.text = self.dataSource[indexPath.row];
cell.buttonTag = indexPath.row;
return cell;
}
、、、、、、
- (void)deleteBtnClicked:(NSInteger)button{
NSLog(@"----------正在删除:%zd行",button);
[self.dataSource removeObjectAtIndex:button];
//错误的写法:
NSIndexPath* path =[NSIndexPath indexPathForRow:button inSection:0];
NSMutableArray* deleteArray=[NSMutableArray arrayWithObject:path];
[self.tableView deleteRowsAtIndexPaths:deleteArray withRowAnimation:UITableViewRowAnimationFade];
}
造成的后果
- 崩溃 :如果所有cell在屏幕的高度范围内可以全部展示出来,这时候删除第一条数据,再删除最后一条数据,会造成崩溃,报错信息是数组越界
- 删除数据不对:如果删除第一个数据后,接着删除第二条数据,会发现实际上是第三条数据被删除了
原因分析
执行deleteRowsAtIndexPaths只会重新执行numberOfRowsInSection方法,而cellForRowAtIndexPath不会重新执行,这使得每个cell的buttontag不变,不会根据删除后的dataSource重新赋值。这时候数据源已经减少,如果点击最后一行的删除按钮,必然会引起数组越界。
如果列表较长,删除数据时需要滑动,这时候就不会出现上述的问题,因为滑动会走cellForRowAtIndexPath方法,会使cell的buttontag属性值及时得到更新。
解决办法一
使用
- (void)deleteNewsBtnClicked:(NSInteger)button{
NSLog(@"----------正在删除:%zd行",button);
[self.dataSource removeObjectAtIndex:button];
// 表格刷新
[self.tableView reloadData];
}
采用这种方法的缺点就是:
- 没有自带删除动画
- reload会消耗更多资源
解决办法二(推荐)
将cell通过代理传出来,再通过cell找到其indexpath
#import <UIKit/UIKit.h>
@class MyCell;
@protocol LKLNewsDelegate <NSObject>
- (void)deleteNewsBtnClicked:(MyCell *)cell;
@end
@interface MyCell : UITableViewCell
@property (nonatomic, assign)id<LKLNewsDelegate>delegate;
@property(nonatomic,strong)UILabel *titleLab;
@end
#import "MyCell.h"
、、、、、、
-(void)rowDelete:(UIButton *)btn{
if (self.delegate && [self.delegate respondsToSelector:@selector(deleteNewsBtnClicked:)]) {
[self.delegate deleteNewsBtnClicked:self];
}
}
#import "ViewController.h"
、、、、、
-(void)deleteNewsBtnClicked:(MyCell *)cell{
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.dataSource removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
这种方法的优点就是没有方法一的缺点 哈哈哈