「連絡先」セルを含む「連絡先リスト」テーブルビューがあり、タップすると、その連絡先の電子メールアドレスを電子メールコンポーザに表示する必要がある電子メールボタンが含まれています。
UIButton をそのセルの「連絡先」インスタンスに関連付ける最良の方法は何ですか?
頭に浮かんだ 2 つのアプローチに対する回答を作成しましたが、満足できるものではありません。あなたはどちらを好みますか、それとももっと良いものを提案してください!
「連絡先」セルを含む「連絡先リスト」テーブルビューがあり、タップすると、その連絡先の電子メールアドレスを電子メールコンポーザに表示する必要がある電子メールボタンが含まれています。
UIButton をそのセルの「連絡先」インスタンスに関連付ける最良の方法は何ですか?
頭に浮かんだ 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 の原則に違反していませんか?
私がよく目にする方法は、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
}
別の解決策:
私にとって、このようなものは完璧に機能し、非常にエレガントに見えます:
- (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 を実装した後、ここで他の人が既に提案している委任パターンを使用することが最善の解決策であることを認める必要があります。
少し迅速な新しいアプローチを見つけたと思います。それについてどう思うか教えてください。
class ButtonCell: UITableViewCell {
var buttonAction: ( () -> Void)?
func buttonPressed() {
self.buttonAction?()
}
}
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()
}
この呼び出し内でデータを渡すこともできます。セルまたはセル内に保存されているもののように。
よろしく、
アレックス
ボタンからセルのビュー階層をトラバースして、セルを決定し、そこからインデックス パスを決定します。
// 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>
別のアプローチを提供します。アクティブなターゲットを割り当てないだけで、イベントはレスポンダー チェーンを通過します。
[self.actionButton addTarget:nil action:@selector(onActionButtonClick:) forControlEvents:UIControlEventTouchUpInside];
次に、View Controllerで次のようにします。
- (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