我有一个“联系人列表”表格视图,其中“联系人”单元格包含一个电子邮件按钮,在点击时,应该向电子邮件编辑人员显示该联系人的电子邮件地址。
将UIButton与该单元格的“联系”实例相关联的最佳方法是什么?
我已经为脑海中浮现的两种方法创造了答案 - 但我并不满意。您更喜欢哪个,或者更好,哪个更好呢!
答案 0 :(得分: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原则吗?
答案 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
;
class ButtonCell: UITableViewCell {
var buttonAction: ( () -> Void)?
func buttonPressed() {
self.buttonAction?()
}
}
您还可以在此调用中传递数据。像细胞或细胞内存储的东西一样。
的问候,
亚历
答案 5 :(得分: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