简介
之前的文章介绍过,UITableView(或UICollectionView)包含大量不同类型的Cell,并且Cell又包括不止一个按钮时,如何有效管理Cell按钮的点击事件。想要实现的是,在控制器中统一处理所有的点击事件。这就要求事件的发送者包含足够多的信息,使控制器能够根据这些信息完成相关的任务(如网络请求等)。在上篇文章介绍过,使用UIView的tag属性可以很方便的携带这些信息(大多数情况下,除非按钮类别数加上Cell的属性数超过了65920个)。
定义按钮类别
首先需要定义所有有可能出现的按钮类别,例如商品按钮、加入购物车按钮等,控制器根据这些类别作出不同的动作,例如点击商品按钮就执行打开商品详情的任务,点击加入购物车按钮,就执行加入购物车的网络请求。一般使用枚举类型定义,代码如下
typedef NS_ENUM (NSInteger, ViewType) {
VIEW_TYPE_MESSAGE = 1,
VIEW_TYPE_SETTING,
VIEW_TYPE_AVATAR,
VIEW_TYPE_NAME,
...
}
建议将这些变量定义在单独的文件中,方便引用。
将row和section传入Cell
仅仅有按钮类别还不足以让控制器找到对应的数据,例如商品按钮代表了页面上的第几个商品等。这时就需要传入额外的参数,即Cell所在的row和section,一般来说有了这些信息,控制器就能够根据数据源找到对应的模型,也就能执行任务了。一般在重用Cell的时候,传入这些参数,同时传入控制器和对应的方法作为点击事件处理者,代码如下
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
CartGoodsCell* cell = [tableView dequeueReusableCellWithIdentifier:GOODS_CELL_REUSE_IDENTIFIER];
[cell addTarget:self
action:@selector (buttonClicked:)
position:indexPath.row
section:indexPath.section];
...
}
Cell保留这些参数,然后在点击事件中使用这些参数
@interface CartGoodsCell () <UITextFieldDelegate>
@property (nonatomic, assign) SEL itemAction;
@property (nonatomic, weak) id itemTarget;
@property (nonatomic, assign) NSInteger itemPosition;
@property (nonatomic, assign) NSInteger itemSection;
@end
@implementation CartGoodsCell
- (void)addTarget:(id _Nonnull)target
action:(SEL _Nonnull)action
position:(NSInteger)position
section:(NSInteger)section {
self.itemTarget = target;
self.itemAction = action;
self.itemPosition = position;
self.itemSection = section;
}
@end
Cell处理点击事件
首先Cell把所有的UIButton,UITextField以及Tap手势事件的目标都指向自己,并设置相应的ViewType。
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)indentifier {
self = [super initWithStyle:style reuseIdentifier:indentifier];
if (self) {
self.goodsImageView.userInteractionEnabled = YES;
UITapGestureRecognizer* tapImage =
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector (onTap:)];
[self.goodsImageView addGestureRecognizer:tapImage];
[self.goodsImageView setViewTypeForTag:VIEW_TYPE_GOODS];
...
}
}
然后在Cell中同意调用之前传入的target和action
- (void)onButtonClicked:(UIView*)sender {
[self callTarget:sender];
}
- (void)textFieldDidEndEditing:(UITextField*)textField {
[self callTarget:textField];
}
- (void)onTap:(UIGestureRecognizer*)gesture {
[self callTarget:gesture.view];
}
- (void)callTarget:(UIView*)sender {
if (![self.itemTarget respondsToSelector:self.itemAction]) {
return;
}
[sender setPositionForTag:self.itemPosition];
[sender setExtraInfoForTag:self.itemSection];
IMP imp = [self.itemTarget methodForSelector:self.itemAction];
void (*function) (id, SEL, UIView*) = (void*)imp;
function (self.itemTarget, self.itemAction, sender);
}
控制器处理点击事件
在控制器中处理点击事件,首先需要从事件发送者获取相关信息,即类别,row,section。然后针对类别执行相关任务,代码如下
- (void)buttonClicked:(UIView*)sender {
ViewType viewType = [sender getViewTypeOfTag];
NSInteger position = [sender getPositionOfTag];
NSInteger section = [sender getExtraInfoOfTag];
switch (viewType) {
case VIEW_TYPE_GOODS: {
ResponseModelCartGoodsModel* goodsModel = [self getGoodsModel:position section:section];
[self openGoodsViewController:goodsModel.sku_id];
break;
}
}
}