编辑
管理复杂页面的按钮点击事件(二)
本文访问次数:0
  1. 1. 简介
  2. 2. 定义按钮类别
  3. 3. 将row和section传入Cell
  4. 4. Cell处理点击事件
  5. 5. 控制器处理点击事件
  1. 管理复杂页面的按钮点击事件
  2. 管理复杂页面的按钮点击事件(二)
  3. 管理复杂页面的按钮点击事件(三)

简介

之前的文章介绍过,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;
        }
    }
}
没有任何评论