Objective c – 处理自定义UITableViewCellbutton的button触摸事件的最佳实践

处理自定义UITableViewCellbutton的button触摸事件的最佳做法是什么?

我的类: MyViewControllerMyCustomCell

我可以想到三个select:

第一个选项 –将button作为MyCustomCell的属性,然后使用MyViewController作为目标在MyViewController .m文件中添加一个目标。

MyViewController .m文件

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"customCell"; MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; [cell.theButton addTarget:self action:@selector(theButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; } // Configure the cell... [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)theButtonTapped:(UIButton *)sender { MyCustomCell *selectedCell = (MyCustomCell *)sender.superview; if (selectedCell) { NSIndexPath *indexPath = [self.tableView indexPathForCell:selectedCell]; MyModel *selectedModel = [self.model objectAtIndex:indexPath.row]; // do something with the model... } } 

第二个选项 –如果自定义单元格是在IB中创build的,则将nib文件的所有者设置为MyViewController ,在MyViewController实现buttonTapped:方法,并将button的Touch Up Inside事件连接到buttonTapped:方法。

第三种select –如果自定义单元格不是在IB中创build的,则将MyCustomCell .m文件中的button添加到MyCustomCell作为目标的button中。
定义MyCustomCellDelegate@property (nonatomic, assign) id<MyCustomCellDelegate> delegateMyCustomCell并在点击button时调用此委托。
在创build单元格和实现MyCustomCellDelegate协议时,将MyViewController设置为单元的委托。

MyCustomCell .h文件

 @class MyCustomCell; @protocol MyCustomCellDelegate <NSObject> - (void)buttonTappedOnCell:(MyCustomCell *)cell; @end @interface MyCustomCell : UITableViewCell @property (nonatomic, retain) UIButton *theButton; @property (nonatomic, assign) id<MyCustomCellDelegate> delegate; @end 

MyCustomCell .m文件

 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // Initialization code self.theButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; self.theButton.frame = CGRectMake(10,10,50,30); [self addSubview:self.theButton]; [self.theButton addTarget:self action:@selector(theButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; } return self; } - (void)theButtonTapped:(UIButton *)sender { [self.delegate buttonTappedOnCell:self]; } 

MyViewController .m文件

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"customCell"; MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.delegate = self; } // Configure the cell... [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)buttonTappedOnCell:(MyCustomCell *)selectedCell { if (selectedCell) { NSIndexPath *indexPath = [self.tableView indexPathForCell:selectedCell]; MyModel *selectedModel = [self.model objectAtIndex:indexPath.row]; // do something with the model... } } 

将单元格的行存储为自定义button的tag属性。

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // bla bla bla if (!cell) { //bla bla bla [cell.yourButton addTarget:self selector:@selector(yourButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; } // bla bla bla cell.yourButton.tag = indexPath.row; } -(void)yourButtonTapped:(id)sender { int tag = [(UIButton *)sender tag]; NSLog(@"tapped button in cell at row %i", tag); } 

从我的angular度来看,使用标签会破坏你的代码的严格性。 另外,当你有多个部分的时候,使用标签肯定会让你的代码变得混乱。

为了避免这个问题,你可以indexPath UITableViewCell并使其拥有一个indexPath属性,让单元知道它的精确位置。

另一个问题是,如果UITableView调用API insertdelete行,则必须更新可见单元格的位置数据

我不认为这是最好的做法。

有一个更好的方法。


我强烈build议在处理Cell中不同的触摸事件时使用MVVM。

在这种模式下,您的自定义UITableViewCell将容纳一个自定义的CellViewModel 。 这个类将负责保存与单元关联的所有数据,因此您可以检索数据并将事件处理逻辑放入单元中。

我通过inheritanceUIButton实现了基于块的方法:

 typedef void (^ActionBlock)(id sender); @interface UIBlockButton : UIButton { ActionBlock _actionBlock; } -(void)handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action; ​@end @implementation UIBlockButton -(void) handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action { _actionBlock = action; [self addTarget:self action:@selector(callActionBlock:) forControlEvents:event]; } -(void) callActionBlock:(id)sender{ _actionBlock(sender); } @end 

并在tableview委托:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (!cell) { cell.yourButton.tag = indexPath.row;// pass tag [cell.yourButton handleControlEvent:UIControlEventTouchUpInside withBlock:^(id sender) { // your selector action code NSLog(@"tapped button in cell at row %i",[(UIButton *)sender tag]); }]; } } 

在某个时候,你的button被点击了,在这个点上它是一个单元格的子视图,它是一些tableview的子视图。

只要编写一个方法,需要一个视图,上超链看到包含的单元格,进一步查找tableview,然后问表索引的单元格的索引。

这比存储一个包含一行的标签要容易得多,也更可靠,因为在编辑tableview的时候你不会遇到问题,而且当你需要indexPath的时候,find它的indexPath就好多了。而不是在一些完全不相关的代码中创build单元格时。

Swift 3.0解决scheme

 cell.btnRequest.tag = indexPath.row cell.btnRequest.addTarget(self,action:#selector(buttonClicked(sender:)), for: .touchUpInside) func buttonClicked(sender:UIButton) { let buttonRow = sender.tag }