Objective c - 为自定义 UITableViewCell 的按钮处理按钮触摸事件的最佳实践
Posted
技术标签:
【中文标题】Objective c - 为自定义 UITableViewCell 的按钮处理按钮触摸事件的最佳实践【英文标题】:Objective c - Best practice to handle a button touch event for a button of a custom UITableViewCell 【发布时间】:2012-05-29 09:46:36 【问题描述】:为自定义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 File's Owner 设置为MyViewController
,在MyViewController
中实现buttonTapped:
方法并连接按钮的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...
【问题讨论】:
我会选择选项 1。这对我来说似乎是最易读的。我使用了类似的方法,但我在自定义单元格中设置了委托,并在自定义单元格本身中处理按钮,然后通过检查委托是否存在来调用视图控制器中的函数。 【参考方案1】:将单元格的行存储为自定义按钮的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);
【讨论】:
这样做有没有好处,而不是我所做的? MyCustomCell *selectedCell = (MyCustomCell *)sender.superview; 上次我检查时,自定义单元格中的按钮会将单元格的内容视图作为超级视图。 按钮应该添加到contentView,其superview是cell。 我用 [self addSubview:button] 将它添加到 MyVustomButton 类中;有问题吗?【参考方案2】:在我看来,使用标签会破坏代码的严格性。此外,当您有多个部分时,使用标签肯定会弄乱您的代码。
为避免此问题,您可以将UITableViewCell
子类化并使其包含indexPath
属性,以让单元格知道其精确位置。
这里的另一个问题是,如果UITableView
调用API 到insert
或delete
行,你必须更新可见单元格的位置数据
我认为这不是最佳做法。
还有更好的办法。
当您必须在 Cell 中处理不同的触摸事件时,我强烈建议您使用 MVVM。
在此模式中,您的自定义 UITableViewCell
将包含自定义 CellViewModel
。此类将负责保存与单元格关联的所有数据,因此您可以检索数据并将事件处理逻辑放入单元格中。
【讨论】:
【参考方案3】:我通过子类化 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]);
];
【讨论】:
【参考方案4】:在某些时候,您的按钮被点击,此时它是一个单元格的子视图,该单元格是某个表格视图的子视图。
只需编写一个获取视图的方法,沿着 superview 链向上查找包含单元格,再向上查找 tableview,然后向 tableview 询问单元格的 indexPath。
这比存储包含行的标签要容易和可靠得多,因为您在编辑 tableview 时不会遇到问题,并且在需要时找出它是哪个 indexPath 的代码要好得多indexPath,而不是在创建单元格时的一些完全不相关的代码中。
【讨论】:
【参考方案5】: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
【讨论】:
以上是关于Objective c - 为自定义 UITableViewCell 的按钮处理按钮触摸事件的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
Objective-C / iOS:为自定义视图子类化 UITableViewController