【问题标题】:Objective c - Best practice to handle a button touch event for a button of a custom UITableViewCellObjective c - 为自定义 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。这对我来说似乎是最易读的。我使用了类似的方法,但我在自定义单元格中设置了委托,并在自定义单元格本身中处理按钮,然后通过检查委托是否存在来调用视图控制器中的函数。

标签: objective-c ios uitableview uibutton


【解决方案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
         }
        

        【讨论】:

          猜你喜欢
          • 2011-09-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多