8

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

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

我已经为我想到的两种方法创建了答案——但我并不真正觉得令人满意。你更喜欢哪个,或者更好,建议更好的!

4

7 回答 7

15

方法二:

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

// 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 原则吗?

于 2013-04-01T14:30:30.870 回答
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
}
于 2013-04-01T17:24:50.473 回答
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];
}

2017 年 12 月 1 日更新

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

于 2014-03-24T14:54:04.467 回答
3

看了这些回答,我想说说我的看法:

逐个按钮位置的单元格

- (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),您可能希望直接发送信息,而无需稍后计算它们,就像以前的解决方案一样。

享受 ;)

于 2014-05-16T18:43:37.627 回答
3

快速关闭方法

我想我找到了一种有点快的新方法。告诉我你对此的看法。

你的手机:

class ButtonCell: UITableViewCell {
    var buttonAction: ( () -> Void)?
    
    func buttonPressed() {
       self.buttonAction?()
   }
}

你的 UITableViewDataSource:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CallCell", for: indexPath)
         //Handled inside 
         cell.buttonAction = {
            //Button pressed 
         }
         //Handle in method
         cell.buttonAction = self.handleInnerCellButtonPress() 
 }

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

问候,
亚历克斯

于 2016-11-22T12:11:04.270 回答
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];
}

对我来说感觉很笨拙。有点“嗯”……</p>

于 2013-04-01T14:29:26.413 回答
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
于 2015-02-06T03:45:10.520 回答