最佳实践:处理tableview单元格内按钮的操作?

时间:2013-04-01 14:29:26

标签: ios uitableview uikit uicollectionview

我有一个“联系人列表”表格视图,其中“联系人”单元格包含一个电子邮件按钮,在点击时,应该向电子邮件编辑人员显示该联系人的电子邮件地址。

将UIButton与该单元格的“联系”实例相关联的最佳方法是什么?

我已经为脑海中浮现的两种方法创造了答案 - 但我并不满意。您更喜欢哪个,或者更好,哪个更好呢!

7 个答案:

答案 0 :(得分:15)

方法2:

让单元格处理操作并调用自定义委托方法。

// YMContactCell.h
@protocol YMContactCellDelegate
- (void)contactCellEmailWasTapped:(YMContactCell*)cell;
@end

@interface YMContactCell
@property (weak, nonatomic) id<YMContactCellDelegate> delegate;
@end

// YMContactCell.m
- (IBAction)emailContact:(id)sender {
    [self.delegate contactCellEmailWasTapped:self];
}

// ContactListViewController.m
- (void)contactCellEmailWasTapped:(YMContactCell*)cell;
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    YMContact *contact = [[self fetchedResultsController] objectAtIndexPath:indexPath];
    // present composer with `contact` ...
}

不处理视图中的事件会违反MVC原则吗?

答案 1 :(得分:7)

我经常看到的方法是将标签分配给等于indexPath.row的按钮。

- (CustomCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    cell.theLabel.text = self.theData[indexPath.row];
    cell.button.tag = indexPath.row;
    [cell.button addTarget:self action:@selector(doSomething:) forControlEvents:UIControlEventTouchUpInside];
    return cell;
}



-(void)doSomething:(UIButton *) sender {
    NSLog(@"%@",self.theData[sender.tag]);
    //sender.tag will be equal to indexPath.row
}

答案 2 :(得分:4)

另一种解决方案:

对我来说,这样的作品完美无瑕,看起来非常优雅:

- (void)buttonClicked:(id)sender
      CGPoint buttonPosition = [sender convertPoint:CGPointZero
                                       toView:self.tableView];
      NSIndexPath *clickedIP = [self.tableView indexPathForRowAtPoint:buttonPosition];

      // When necessary 
      // UITableViewCell *clickedCell = [self.tableView cellForRowAtIndexPath:clickedIP];
}

更新12/01/2017

过了一段时间,并且实现了很多UITableViews,我需要承认最好的解决方案是使用委托模式,这已经是其他人在这里提出的。

答案 3 :(得分:2)

阅读这些答案,我想说出我的意见:

单元格按钮位置

- (void)buttonClicked:(id)sender
  CGPoint buttonPosition = [sender convertPoint:CGPointZero
                                   toView:self.tableView];
  NSIndexPath *clickedIP = [self.tableView indexPathForRowAtPoint:buttonPosition];

  // When necessary 
  // UITableViewCell *clickedCell = [self.tableView cellForRowAtIndexPath:clickedIP];
}

上述解决方案肯定是实施最快的,但从设计/架构的角度来看,它不是最好的。此外,您获得indexPath但需要计算任何其他信息。这是一种很酷的方法,但不是最好的方法。

按钮超级视图上的单元格循环

// ContactListViewController.m

- (IBAction)emailContact:(id)sender {
    YMContact *contact = [self contactFromContactButton:sender];
    // present composer with `contact`...
}

- (YMContact *)contactFromContactButton:(UIView *)contactButton {
    UIView *aSuperview = [contactButton superview];
    while (![aSuperview isKindOfClass:[UITableViewCell class]]) {
        aSuperview = [aSuperview superview];
    }
    YMContactCell *cell = (id) aSuperview;
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    return [[self fetchedResultsController] objectAtIndexPath:indexPath];
}

以这种方式获取单元格比以前更昂贵,并且它也不优雅。

单元格按钮标记

- (CustomCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    cell.theLabel.text = self.theData[indexPath.row];
    cell.button.tag = indexPath.row;
    [cell.button addTarget:self action:@selector(doSomething:) forControlEvents:UIControlEventTouchUpInside];
    return cell;
}

-(void)doSomething:(UIButton *) sender {
    NSLog(@"%@",self.theData[sender.tag]);
    //sender.tag will be equal to indexPath.row
}

绝对没有。使用标签似乎是一个很酷的解决方案,但控件的标签可用于很多事情,如下一个响应者等我不喜欢这个不是正确的方式

按设计模式划分的单元格

// YMContactCell.h
@protocol YMContactCellDelegate
- (void)contactCellEmailWasTapped:(YMContactCell*)cell;
@end

@interface YMContactCell
@property (weak, nonatomic) id<YMContactCellDelegate> delegate;
@end

// YMContactCell.m
- (IBAction)emailContact:(id)sender {
    [self.delegate contactCellEmailWasTapped:self];
}

// ContactListViewController.m
- (void)contactCellEmailWasTapped:(YMContactCell*)cell;
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    YMContact *contact = [[self fetchedResultsController] objectAtIndexPath:indexPath];
    // present composer with `contact` ...
}

这是解决方案。使用delegation或使用blocks确实是一件好事,因为您可以传递所需的所有参数并使架构可扩展。事实上,在delegate方法中(但也包含blocks),您可能希望直接发送信息而无需在以后计算它们,就像之前的解决方案一样。

享受;)

答案 4 :(得分:2)

快速关闭方法

我想我发现了一种有点过时的新方法。告诉我你的想法。

你的手机:

select  case when count(*) = count(nullif(flag1,'N')) then 'True' else 'False' end as f1
       ,case when count(*) = count(nullif(flag2,'N')) then 'True' else 'False' end as f2
       ,case when count(*) = count(nullif(flag3,'N')) then 'True' else 'False' end as f3

from    t 
;

您的UITableViewDataSource:

class ButtonCell: UITableViewCell {
    var buttonAction: ( () -> Void)?

    func buttonPressed() {
       self.buttonAction?()
   }
}

您还可以在此调用中传递数据。像细胞或细胞内存储的东西一样。

的问候,
亚历

答案 5 :(得分:1)

方法1:

通过从按钮遍历单元格的视图层次结构来确定单元格以及索引路径。

// ContactListViewController.m

- (IBAction)emailContact:(id)sender {
    YMContact *contact = [self contactFromContactButton:sender];
    // present composer with `contact`...
}

- (YMContact *)contactFromContactButton:(UIView *)contactButton {
    UIView *aSuperview = [contactButton superview];
    while (![aSuperview isKindOfClass:[UITableViewCell class]]) {
        aSuperview = [aSuperview superview];
    }
    YMContactCell *cell = (id) aSuperview;
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    return [[self fetchedResultsController] objectAtIndexPath:indexPath];
}

我觉得很笨。有点“meh”......

答案 6 :(得分:0)

我会提供另一种方法,只是没有分配活动目标,事件将遍历响应者链:

[self.actionButton addTarget:nil action:@selector(onActionButtonClick:) forControlEvents:UIControlEventTouchUpInside];

然后,在您的视图控制器中:

- (void)onActorButtonClick:(id)sender {
  if ([sender isKindOfClass:UIButton.class]) {
    UITableViewCell *cell = [self findAncestorTableCell:(UIView *)sender]; //See other answer to fetch the cell instance.
    NSIndexPath *indexPath = [self.listTable indexPathForCell:cell];
    ...
  }
}

然而,这会产生一些编译器警告,添加它以忽略它们:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
...
#pragma clang diagnostic pop