处理自定义UITableViewCell
按钮的按钮触摸事件的最佳做法是什么?
我的课程:
MyViewController
,MyCustomCell
我可以想到三个选择:
第一个选项 - 将该按钮作为MyCustomCell
的属性,然后在MyViewController
.m文件中添加一个目标,其中MyViewController
为目标。
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中创建的,请将nib文件的所有者设置为MyViewController
,在buttonTapped:
中实现MyViewController
方法并连接按钮的Touch Up Inside事件为buttonTapped:
方法。
第三个选项 - 如果未在IB中制作自定义单元格,请将目标添加到MyCustomCell
.m文件中以MyCustomCell
为目标的按钮。
定义MyCustomCellDelegate
添加@property (nonatomic, assign) id<MyCustomCellDelegate> delegate
到MyCustomCell
,并在按下按钮时调用此代理。
在创建单元格时设置MyViewController
作为单元格的委托,并实现MyCustomCellDelegate
协议。
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...
}
}
答案 0 :(得分:11)
将单元格的行存储为自定义按钮的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);
}
答案 1 :(得分:1)
从我的角度来看,使用标记会破坏代码的严格性。此外,当你有多个部分时,使用标签肯定会使你的代码混乱。
要避免此问题,您可以继承UITableViewCell
并使其保留indexPath
属性,让单元格知道其精确位置。
此处的另一个问题是,如果UITableView
调用API到insert
或delete
行,则必须更新可见的单元格&#39;位置数据
我认为这不是最好的做法。
有更好的方法。
我强烈建议您在单元格中处理不同的触摸事件时使用MVVM。
在此模式中,您的自定义UITableViewCell
会包含自定义CellViewModel
。此类将负责保存与单元格关联的所有数据,因此您可以检索数据并将事件处理逻辑放在单元格中。
答案 2 :(得分:0)
我通过子类化UIButton实现了基于块的方法:
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]);
}];
}
}
答案 3 :(得分:0)
在某些时候点击你的按钮,此时它是一个单元格的子视图,它是某个tableview的子视图。
只需编写一个获取视图的方法,在superview链上查找包含的单元格,进一步查找tableview,然后向tableview询问单元格的indexPath。
这比存储包含行的标记更容易,更可靠,因为在编辑tableview时不会遇到问题,并且让代码在需要时找出哪个indexPath要好得多indexPath,而不是在创建单元格时的某些完全不相关的代码。
答案 4 :(得分:0)
Swift 3.0解决方案
cell.btnRequest.tag = indexPath.row
cell.btnRequest.addTarget(self,action:#selector(buttonClicked(sender:)), for: .touchUpInside)
func buttonClicked(sender:UIButton) {
let buttonRow = sender.tag
}