可以看到,本来点击的是第二项的➕号,但是,却变成第一项增加了,在模拟器上跑了一次:
正常运行,没有任何问题,应该是系统的原因造成的。
刚好最近弄了部iphone4,跑着iOS7的系统,重新跑了一次,果然,bug重现了,看下我们的代码:
cell.changeCarNumView.incrementCallback = ^(PKYStepper *stepper, float newValue){
@strongify(self);
if (!self) return;
AfternoonTeaShopCell *superCell = (AfternoonTeaShopCell *)stepper.superview.superview;
NSIndexPath *indexPath = [self.kitchenDetailsView.tableView indexPathForCell:superCell];
KitchenDetailsTodayMenuModel *model = self.todayMenuArray[indexPath.row];
model.selectCount = newValue;
[self.kitchenDetailsView.tableView reloadRowAtIndexPath:indexPath withRowAnimation:UITableViewRowAnimationNone];
};
这里,stepper
是加在AfternoonTeaShopCell
的contentView
里面,所以这里可以使用stepper.superview.superview
来获取cell
,然后获取cell
相对应的indexPath
,改变数量,刷新当前行.
这代码在iOS8(包括iOS8)运行良好,但是在iOS7却出现了问题,加了个断点,当我们点击加号的时候:
What the fuck!!!在当初你告诉我,见过你父亲和你爷爷就可以娶到你,此时此刻,当我历尽千辛万苦,来到你爷爷面前,桃花依旧,人不再,你爷爷说要再见过你曾祖父才行。
从上面的截图可以看出,在iOS7上和iOS9上,UITableViewCell的层次已经发生了改变,在iOS7上,我们可以看到多了一层UITableViewCellScrollvView
,所以在iOS7上我们需要使用三个superview
来获取cell
,iOS7 UITableViewCell的变化
也阐述了iOS6-iOS7UITableViewCell视图层次的改变,但是在iOS8之后又变回了,所以:
if (IOS_VERSION >= 7.0 && IOS_VERSION < 8.0) {
AfternoonTeaShopCell *superCell = (AfternoonTeaShopCell *)stepper.superview.superview.superview;
}else{
AfternoonTeaShopCell *superCell = (AfternoonTeaShopCell *)stepper.superview.superview;
}
当然,我们也可以使用stackoverflow上的一种方法:
UIView *view = stepper;
while (view != nil && ![view isKindOfClass:[UITableViewCell class]]) {
view = [view superview];
}
AfternoonTeaShopCell *superCell = (AfternoonTeaShopCell *)view;
总算解决了bug,但是一切的源头还是由于我换了另一种方法去操作,并且考虑不周。
给UITableViewCell的按钮添加事件有三种方法:
Tag方式
通过给button
设置tag
,一般设置为indexPath.row
[cell.delBtn addTarget:self action:@selector(delItem:) forControlEvents:UIControlEventTouchUpInside];
cell.delBtn.tag = indexPath.row;
然后,在点击事件delItem
中:
- (void)delItem:(UIButton *)btn{
NSInteger index = btn.tag;//1
[self.dataArray removeObjectAtIndex:index];
[self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationMiddle];
}
通过btn.tag
获取我们当前操作了第几行,然后remove
数据源(dataArray)对应的数据,再使用deleteRowsAtIndexPaths
给tableview
添加一个删除动画,一切没看来是那么perfect,让我们看看效果,运行:
还好没抱太大希望,不然会得到生命不能承受之重的绝望,当我们点击第一个删除按钮的时候,一切都是正常的,当我们点击第二个删除按钮的时候,我们点击的是title->1
这一行,但是却是把title->2
删除了,当我们点击最后一行的时候,直接crash。
这里我们将delItem
的代码修改一下,将:
[self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationMiddle];
改为:
[self.tableView reloadData];
运行:
一切正常,就是没有了删除动画,聪明的你应该已经知道答案了吧。
当我们使用deleteRowsAtIndexPaths
方法时:
- 首先删除了第0行,这时候之前的第一行(title->1)成为了现在的第0行
- 当我们点击第0行(title->1)这行的删除按钮时,这时候我们需要的是删除第0行的数据,但是要注意一点,我们在
cellForRowAtIndexPath
给button
设置的tag
就是当前的行数,而我们调用的deleteRowsAtIndexPaths
方法是不会调cellForRowAtIndexPath
的,所以,我们删除了(title->0)这行之后,我们当前的(title->1)的tag
还是1,而非我们想要的0,所以,这里会把(title->1)下面那行删除掉 当我们点击最后一行的时候,crash,我们看一张图:
此时数据源(dataArray)的
count
是7,但是我们的index
却是9,所以是由数组越界引起的crash.
当我们使用reloadData
时
因为每次删除操作之后都调用了cellForRowAtIndexPath
,所以保证了button.tag
的值是最新的,所以可以正常运行,但是对于删除的操作就显得突兀,没有过渡动画.
Cell.superview方式
使用这种方式,我们不需要给button
设置tag
,下面是代码:
- (void)delItem:(UIButton *)btn{
UIView *view = btn;
while (view != nil && ![view isKindOfClass:[UITableViewCell class]]) {
view = [view superview];
}
Cell *cell = (Cell *)view;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.dataArray removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}
delegate方式
通过delegate
的方式,将button
的点击事件回传.
delegate:
@protocol CellDelegate <NSObject>
- (void)delBtnClick:(DelegateCell *)cell;
@end
在DelegateCell
的删除按钮点击事件中:
- (IBAction)delClick:(UIButton *)sender {
if ([self.delegate respondsToSelector:@selector(delBtnClick:)]) {
[self.delegate delBtnClick:self];
}
}
记得在cellForRowAtIndexPath
中设置:
cell.delegate = self;
最后,在当前控制器实现delegate
的delBtnClick
方法:
- (void)delBtnClick:(DelegateCell *)cell{
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.dataArray removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}
这种方式和Cell.superview
方式类似,都是为了获取当前的cell
,然后获得indexPath
,再删除。
好了,以上就是UITableViewCell
的删除按钮处理事件的几种方式,推荐使用后两种方式来进行操作,第一种虽然代码比较少,但是很难在保证tag
正确性的前提下加入删除动画,当然你也可以在删除动画完成后再使用reloadData
刷新列表,但是这没必要不是吗?如果你对第一种有更好的方法解决请一定要告诉我,我还是刚起飞的菜鸟,很多事情站在我的角度会看不全。