Detectando qual UIButton foi pressionado em um UITableView

Eu tenho um UITableView com 5 UITableViewCells . Cada célula contém um UIButton que é configurado da seguinte maneira:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *identifier = @"identifier"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (cell == nil) { cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; [cell autorelelase]; UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)]; [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside]; [button setTag:1]; [cell.contentView addSubview:button]; [button release]; } UIButton *button = (UIButton *)[cell viewWithTag:1]; [button setTitle:@"Edit" forState:UIControlStateNormal]; return cell; } 

Minha pergunta é a seguinte: no método buttonPressedAction: como sei qual botão foi pressionado. Eu considerei o uso de tags, mas não tenho certeza se esse é o melhor caminho. Eu gostaria de poder marcar de alguma forma o indexPath no controle.

 - (void)buttonPressedAction:(id)sender { UIButton *button = (UIButton *)sender; // how do I know which button sent this message? // processing button press for this row requires an indexPath. } 

Qual é a maneira padrão de fazer isso?

Editar:

Eu meio que resolvi isso fazendo o seguinte. Eu ainda gostaria de ter uma opinião se esta é a maneira padrão de fazê-lo ou existe uma maneira melhor?

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *identifier = @"identifier"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (cell == nil) { cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; [cell autorelelase]; UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)]; [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside]; [cell.contentView addSubview:button]; [button release]; } UIButton *button = (UIButton *)[cell.contentView.subviews objectAtIndex:0]; [button setTag:indexPath.row]; [button setTitle:@"Edit" forState:UIControlStateNormal]; return cell; } - (void)buttonPressedAction:(id)sender { UIButton *button = (UIButton *)sender; int row = button.tag; } 

O que é importante notar é que não posso definir a tag na criação da célula, já que a célula pode ser removida. Parece muito sujo. Deve haver um caminho melhor.

   

Na amostra de acessório da Apple, o seguinte método é usado:

 [button addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; 

Em seguida, a coordenada de toque do manipulador em toque é recuperada e o caminho do índice é calculado a partir dessa coordenada:

 - (void)checkButtonTapped:(id)sender { CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition]; if (indexPath != nil) { ... } } 

Eu encontrei o método de usar superview do superview para obter uma referência ao indexPath da célula funcionou perfeitamente. Obrigado a iphonedevbook.com (macnsmith) pelo texto do link da dica

 -(void)buttonPressed:(id)sender { UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview]; NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell]; ... } 

Aqui está como eu faço isso. Simples e conciso:

 - (IBAction)buttonTappedAction:(id)sender { CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition]; ... 

Encontrei uma boa solução para esse problema em outro lugar, sem mexer nas tags no botão:

 - (void)buttonPressedAction:(id)sender { NSSet *touches = [event allTouches]; UITouch *touch = [touches anyObject]; CGPoint currentTouchPosition = [touch locationInView:self.tableView]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition]; do stuff with the indexPath... } 

Como sobre o envio de informações como NSIndexPath no UIButton usando injeção de tempo de execução.

1) Você precisa de tempo de execução na importação

2) adicionar constante estática

3) adicione NSIndexPath ao seu botão em tempo de execução usando:

(void) setMetaData: (id) target withObject: (id) newObj

4) no botão pressione get metadata usando:

(id) metaData: (id) target

Apreciar

  #import  static char const * const kMetaDic = "kMetaDic"; #pragma mark - Getters / Setters - (id)metaData:(id)target { return objc_getAssociatedObject(target, kMetaDic); } - (void)setMetaData:(id)target withObject:(id)newObj { objc_setAssociatedObject(target, kMetaDic, newObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #On the cell constructor - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { .... cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; .... [btnSocial addTarget:self action:@selector(openComments:) forControlEvents:UIControlEventTouchUpInside]; #add the indexpath here or another object [self setMetaData:btnSocial withObject:indexPath]; .... } #The action after button been press: - (IBAction)openComments:(UIButton*)sender{ NSIndexPath *indexPath = [self metaData:sender]; NSLog(@"indexPath: %d", indexPath.row); //Reuse your indexpath Now } 
 func buttonAction(sender:UIButton!) { var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tablevw) let indexPath = self.tablevw.indexPathForRowAtPoint(position) let cell: TableViewCell = tablevw.cellForRowAtIndexPath(indexPath!) as TableViewCell println(indexPath?.row) println("Button tapped") } 

Para fazer (@Vladimir) a resposta é Swift:

 var buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView) var indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)! 

Embora a verificação de indexPath != nil me dê o dedo … “NSIndexPath não é um subtipo de NSString”

Eu usaria a propriedade tag como você disse, definindo a tag da seguinte forma:

 [button setTag:indexPath.row]; 

em seguida, obtendo a tag dentro do buttonPressedAction da seguinte forma:

 ((UIButton *)sender).tag 

Ou

 UIButton *button = (UIButton *)sender; button.tag; 

Embora eu goste da tag … se você não quiser usar tags por qualquer motivo, você pode criar um membro NSArray de botões pré-criados:

 NSArray* buttons ; 

em seguida, crie esses botões antes de renderizar o tableView e empurrá-los para o array.

Então, dentro da function tableView:cellForRowAtIndexPath: você pode fazer:

 UIButton* button = [buttons objectAtIndex:[indexPath row] ] ; [cell.contentView addSubview:button]; 

Então, na function buttonPressedAction: você pode fazer

 - (void)buttonPressedAction:(id)sender { UIButton* button = (UIButton*)sender ; int row = [buttons indexOfObject:button] ; // Do magic } 

PARA MANUSEAR SEÇÕES – Eu armazenei o NSIndexPath em um UITableViewCell personalizado

EM CLKIndexPricesHEADERTableViewCell.xib

IN IB Adicione o UIButton ao XIB – NÃO adicione ação!

Adicionar saída @property (reter, nonatomic) IBOutlet UIButton * buttonIndexSectionClose;

NÃO CTRL + DRAG uma ação no IB (feito no código abaixo)

 @interface CLKIndexPricesHEADERTableViewCell : UITableViewCell ... @property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose; @property (nonatomic, retain) NSIndexPath * indexPathForCell; @end 

Em viewForHeaderInSection (também deve funcionar para cellForRow …. etc se a tabela tiver apenas 1 seção)

 - viewForHeaderInSection is called for each section 1...2...3 - get the cell CLKIndexPricesHEADERTableViewCell - getTableRowHEADER just does the normal dequeueReusableCellWithIdentifier - STORE the indexPath IN the UITableView cell - indexPath.section = (NSInteger)section - indexPath.row = 0 always (we are only interested in sections) - (UIView *) tableView:(UITableView *)tableView1 viewForHeaderInSection:(NSInteger)section { //Standard method for getting a UITableViewCell CLKIndexPricesHEADERTableViewCell * cellHEADER = [self getTableRowHEADER]; 

… use a seção para obter dados para o seu celular

… preencha

  indexName = ffaIndex.routeCode; indexPrice = ffaIndex.indexValue; // [cellHEADER.buttonIndexSectionClose addTarget:self action:@selector(buttonDELETEINDEXPressedAction:forEvent:) forControlEvents:UIControlEventTouchUpInside]; cellHEADER.indexPathForCell = [NSIndexPath indexPathForRow:0 inSection:section]; return cellHEADER; } 

USER pressiona o botão DELETE em um header da seção e essas chamadas

 - (void)buttonDELETEINDEXPressedAction:(id)sender forEvent:(UIEvent *)event { NSLog(@"%s", __PRETTY_FUNCTION__); UIView * parent1 = [sender superview]; // UiTableViewCellContentView //UIView *myContentView = (UIView *)parent1; UIView * parent2 = [parent1 superview]; // custom cell containing the content view //UIView * parent3 = [parent2 superview]; // UITableView containing the cell //UIView * parent4 = [parent3 superview]; // UIView containing the table if([parent2 isMemberOfClass:[CLKIndexPricesHEADERTableViewCell class]]){ CLKIndexPricesHEADERTableViewCell *myTableCell = (CLKIndexPricesHEADERTableViewCell *)parent2; //UITableView *myTable = (UITableView *)parent3; //UIView *mainView = (UIView *)parent4; NSLog(@"%s indexPath.section,row[%d,%d]", __PRETTY_FUNCTION__, myTableCell.indexPathForCell.section,myTableCell.indexPathForCell.row); NSString *key = [self.sortedKeysArray objectAtIndex:myTableCell.indexPathForCell.section]; if(key){ NSLog(@"%s DELETE object at key:%@", __PRETTY_FUNCTION__,key); self.keyForSectionIndexToDelete = key; self.sectionIndexToDelete = myTableCell.indexPathForCell.section; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Remove Index" message:@"Are you sure" delegate:self cancelButtonTitle:@"No" otherButtonTitles:@"Yes", nil]; alertView.tag = kALERTVIEW_REMOVE_ONE_INDEX; [alertView show]; [alertView release]; //------ }else{ NSLog(@"ERROR: [%s] key is nil for section:%d", __PRETTY_FUNCTION__,myTableCell.indexPathForCell.section); } }else{ NSLog(@"ERROR: [%s] CLKIndexPricesHEADERTableViewCell not found", __PRETTY_FUNCTION__); } } 

Neste exemplo eu adicionei um botão Delete, então deve mostrar UIAlertView para confirmar

Eu guardo a seção e chave no dictionary que armazena informação sobre a seção em um ivar no VC

 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if(alertView.tag == kALERTVIEW_REMOVE_ONE_INDEX){ if(buttonIndex==0){ //NO NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex); //do nothing } else if(buttonIndex==1){ //YES NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex); if(self.keyForSectionIndexToDelete != nil){ //Remove the section by key [self.indexPricesDictionary removeObjectForKey:self.keyForSectionIndexToDelete]; //sort the keys so sections appear alphabetically/numbericsearch (minus the one we just removed) [self updateTheSortedKeysArray]; //Delete the section from the table using animation [self.tableView beginUpdates]; [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:self.sectionIndexToDelete] withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView endUpdates]; //required to trigger refresh of myTableCell.indexPathForCell else old values in UITableViewCells [self.tableView reloadData]; }else{ NSLog(@"ERROR: [%s] OBJECT is nil", __PRETTY_FUNCTION__); } } else { NSLog(@"ERROR: [%s] UNHANDLED BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex); } }else { NSLog(@"ERROR: [%s] unhandled ALERTVIEW TAG:%d", __PRETTY_FUNCTION__,alertView.tag); } } 
 A better way would be to subclass your button and add a indexPath property to it. //Implement a subclass for UIButton. @interface NewButton:UIButton @property(nonatomic, strong) NSIndexPath *indexPath; Make your button of type NewButton in the XIB or in the code whereever you are initializing them. Then in the cellForRowAtIndexPath put the following line of code. button.indexPath = indexPath; return cell; //As usual Now in your IBAction -(IBAction)buttonClicked:(id)sender{ NewButton *button = (NewButton *)sender; //Now access the indexPath by buttons property.. NSIndexPath *indexPath = button.indexPath; //:) } 

Isso funciona para mim também, obrigado @Cocoanut

Eu encontrei o método de usar superview do superview para obter uma referência ao indexPath da célula funcionou perfeitamente. Obrigado a iphonedevbook.com (macnsmith) pelo texto do link da dica

 -(void)buttonPressed:(id)sender { UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview]; NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell]; ... } 

você pode usar o padrão de tags:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *identifier = @"identifier"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (cell == nil) { cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; [cell autorelelase]; UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)]; [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside]; [button setTag:[indexPath row]]; //use the row as the current tag [cell.contentView addSubview:button]; [button release]; } UIButton *button = (UIButton *)[cell viewWithTag:[indexPath row]]; //use [indexPath row] [button setTitle:@"Edit" forState:UIControlStateNormal]; return cell; } - (void)buttonPressedAction:(id)sender { UIButton *button = (UIButton *)sender; //button.tag has the row number (you can convert it to indexPath) } 

Estou esquecendo de algo? Você não pode simplesmente usar o remetente para identificar o botão. O remetente lhe dará informações como esta:

 > 

Então, se você quiser alterar as propriedades do botão, diga a imagem de fundo que acabou de informar ao remetente:

 [sender setBackgroundImage:[UIImage imageNamed:@"new-image.png"] forState:UIControlStateNormal]; 

Se você precisar da tag, o método do ACBurk é bom.

 // how do I know which button sent this message? // processing button press for this row requires an indexPath. 

Bem simples, na verdade:

 - (void)buttonPressedAction:(id)sender { UIButton *button = (UIButton *)sender; CGPoint rowButtonCenterInTableView = [[rowButton superview] convertPoint:rowButton.center toView:self.tableView]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rowButtonCenterInTableView]; MyTableViewItem *rowItem = [self.itemsArray objectAtIndex:indexPath.row]; // Now you're good to go.. do what the intention of the button is, but with // the context of the "row item" that the button belongs to [self performFooWithItem:rowItem]; } 

Trabalhando bem para mim: P

Se você quiser ajustar sua configuração de ação de destino, poderá include o parâmetro de evento no método e, em seguida, usar os toques desse evento para resolver as coordenadas do toque. As coordenadas ainda precisam ser resolvidas nos limites da exibição de toques, mas isso pode parecer mais fácil para algumas pessoas.

crie um array nsmutable e coloque todos os botões nesse array usint [array addObject: yourButton];

no método de pressionar o botão

  (void)buttonPressedAction:(id)sender { UIButton *button = (UIButton *)sender; for(int i=0;i< [yourArray count];i++){ if([buton isEqual:[yourArray objectAtIndex:i]]){ //here write wat u need to do } } 

Uma pequena variação na resposta do Cocoanuts (que me ajudou a resolver isso) quando o botão estava no rodapé de uma tabela (o que impede você de encontrar a ‘célula clicada’):

 -(IBAction) buttonAction:(id)sender; { id parent1 = [sender superview]; // UiTableViewCellContentView id parent2 = [parent1 superview]; // custom cell containing the content view id parent3 = [parent2 superview]; // UITableView containing the cell id parent4 = [parent3 superview]; // UIView containing the table UIView *myContentView = (UIView *)parent1; UITableViewCell *myTableCell = (UITableViewCell *)parent2; UITableView *myTable = (UITableView *)parent3; UIView *mainView = (UIView *)parent4; CGRect footerViewRect = myTableCell.frame; CGRect rect3 = [myTable convertRect:footerViewRect toView:mainView]; [cc doSomethingOnScreenAtY:rect3.origin.y]; } 

Eu sempre uso tags.

Você precisa subclassificar o UITableviewCell e manipular o pressionamento de botão a partir daí.

É simples; faça uma célula customizada e pegue uma saída de botão

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *identifier = @"identifier"; customCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; cell.yourButton.tag = indexPath.Row; - (void)buttonPressedAction:(id)sender 

alterar id no método acima para (UIButton *)

Você pode obter o valor que botão está sendo tocado, fazendo sender.tag.

Subclass o botão para armazenar o valor requerido, talvez crie um protocolo (ControlWithData ou algo assim). Defina o valor quando você adiciona o botão à célula de exibição de tabela. Em seu evento de retoque, veja se o remetente obedece ao protocolo e extrai os dados. Eu normalmente armazeno uma referência ao object real que é renderizado na célula da visão de tabela.

SWIFT 2 UPDATE

Veja como descobrir qual botão foi acionado + enviar dados para outro ViewController a partir do indexPath.row desse botão, já que estou assumindo que esse é o ponto para a maioria!

 @IBAction func yourButton(sender: AnyObject) { var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView) let indexPath = self.tableView.indexPathForRowAtPoint(position) let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as UITableViewCell print(indexPath?.row) print("Tap tap tap tap") } 

Para aqueles que estão usando uma class ViewController e adicionaram um tableView, estou usando um ViewController em vez de um TableViewController, então adicionei manualmente o tableView para acessá-lo.

Aqui está o código para passar dados para outro VC ao tocar nesse botão e passar o indexPath.row da célula.

 @IBAction func moreInfo(sender: AnyObject) { let yourOtherVC = self.storyboard!.instantiateViewControllerWithIdentifier("yourOtherVC") as! YourOtherVCVIewController var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView) let indexPath = self.tableView.indexPathForRowAtPoint(position) let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as UITableViewCell print(indexPath?.row) print("Button tapped") yourOtherVC.yourVarName = [self.otherVCVariable[indexPath!.row]] self.presentViewController(yourNewVC, animated: true, completion: nil) } 

Note aqui que eu estou usando a célula personalizada este código está funcionando perfeitamente para mim

  @IBAction func call(sender: UIButton) { var contentView = sender.superview; var cell = contentView?.superview as EmployeeListCustomCell if (!(cell.isKindOfClass(EmployeeListCustomCell))) { cell = (contentView?.superview)?.superview as EmployeeListCustomCell } let phone = cell.lblDescriptionText.text! //let phone = detailObject!.mobile! let url:NSURL = NSURL(string:"tel://"+phone)!; UIApplication.sharedApplication().openURL(url); } 

A solução de Chris Schwerdt, mas depois no Swift, funcionou para mim:

 @IBAction func rateButtonTapped(sender: UIButton) { let buttonPosition : CGPoint = sender.convertPoint(CGPointZero, toView: self.ratingTableView) let indexPath : NSIndexPath = self.ratingTableView.indexPathForRowAtPoint(buttonPosition)! print(sender.tag) print(indexPath.row) } 

Esse problema tem duas partes:

1) Obtendo o caminho do índice do UITableViewCell que contém o UIButton pressionado

Existem algumas sugestões como:

  • Atualizando a tag do cellForRowAtIndexPath: em cellForRowAtIndexPath: método usando o valor da row do caminho do índice. Esta não é uma boa solução, pois exige a atualização da tag continuamente e não funciona com as exibições de tabela com mais de uma seção.

  • Adicionando uma propriedade NSIndexPath à célula personalizada e atualizando-a em vez da tag do cellForRowAtIndexPath: no método cellForRowAtIndexPath: Isso resolve vários problemas de seção, mas ainda não é bom, já que requer atualização sempre.

  • Mantendo um refence fraco para o UITableView pai na célula customizada ao criá-lo e usando o método indexPathForCell: para obter o caminho do índice. Parece um pouco melhor, não há necessidade de atualizar nada no método cellForRowAtIndexPath: mas ainda requer a definição de uma referência fraca quando a célula personalizada é criada.

  • Usando a propriedade superView da célula para obter uma referência ao UITableView pai. Não é necessário adicionar nenhuma propriedade à célula personalizada e não é necessário definir / atualizar nada na criação / posterior. Mas o superView da célula depende dos detalhes da implementação do iOS. Por isso não pode ser usado diretamente.

Mas isso pode ser conseguido usando um simples loop, pois temos certeza de que a célula em questão deve estar em um UITableView:

 UIView* view = self; while (view && ![view isKindOfClass:UITableView.class]) view = view.superview; UITableView* parentTableView = (UITableView*)view; 

Portanto, essas sugestões podem ser combinadas em um método de célula personalizado simples e seguro para obter o caminho do índice:

 - (NSIndexPath *)indexPath { UIView* view = self; while (view && ![view isKindOfClass:UITableView.class]) view = view.superview; return [(UITableView*)view indexPathForCell:self]; } 

De agora em diante, esse método pode ser usado para detectar qual UIButton é pressionado.

2) Informando outras partes sobre evento de imprensa de botão

Depois de saber internamente qual UIButton é pressionado em qual célula personalizada com o caminho de índice exato, essa informação precisa ser enviada para outras partes (provavelmente o controlador de exibição manipulando o UITableView ). Portanto, esse evento de clique de botão pode ser tratado em um nível de abstração e lógica semelhante ao método didSelectRowAtIndexPath: method of UITableView.

Duas abordagens podem ser usadas para isso:

a) Delegação: célula customizada pode ter uma propriedade delegate e pode definir um protocolo. Quando o botão é pressionado, ele apenas executa seus methods delegates em sua propriedade delegate . Mas essa propriedade de delegate precisa ser definida para cada célula personalizada quando elas são criadas. Como alternativa, a célula personalizada também pode optar por executar seus methods delegates no delegate da visualização da tabela pai.

b) Centro de Notificação: as células customizadas podem definir um nome de notificação customizado e postar esta notificação com as informações do caminho do índice e da tabela pai fornecidas no object userInfo . Não é necessário definir nada para cada célula, basta adicionar um observador para a notificação da célula personalizada.

Eu uso uma solução que subclass UIButton e eu pensei que deveria apenas compartilhá-lo aqui, códigos em Swift:

 class ButtonWithIndexPath : UIButton { var indexPath:IndexPath? } 

Então lembre-se de atualizar seu indexPath em cellForRow(at:)

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let returnCell = tableView.dequeueReusableCell(withIdentifier: "cellWithButton", for: indexPath) as! cellWithButton ... returnCell.button.indexPath = IndexPath returnCell.button.addTarget(self, action:#selector(cellButtonPressed(_:)), for: .touchUpInside) return returnCell } 

Então, ao responder ao evento do botão, você pode usá-lo como

 func cellButtonPressed(_ sender:UIButton) { if sender is ButtonWithIndexPath { let button = sender as! ButtonWithIndexPath print(button.indexPath) } } 

Com o Swift 4.2 e iOS 12, o UITableView possui um método chamado indexPathForRow(at:) . indexPathForRow(at:) tem a seguinte declaração:

 func indexPathForRow(at point: CGPoint) -> IndexPath? 

Retorna um caminho de índice identificando a linha e a seção no ponto dado.


O código completo a seguir mostra como implementar o indexPathForRow(at:) para obter o IndexPath correspondente de um UIButton localizado em um UITableViewCell :

 import UIKit class CustomCell: UITableViewCell { let button = UIButton(type: .system) override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) button.setTitle("Tap", for: .normal) contentView.addSubview(button) button.translatesAutoresizingMaskIntoConstraints = false button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true button.topAnchor.constraintEqualToSystemSpacingBelow(contentView.topAnchor, multiplier: 1).isActive = true button.leadingAnchor.constraintGreaterThanOrEqualToSystemSpacingAfter(contentView.leadingAnchor, multiplier: 1).isActive = true } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 
 import UIKit class TableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() tableView.register(CustomCell.self, forCellReuseIdentifier: "Cell") } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell cell.button.addTarget(self, action: #selector(printIndexPath), for: .touchUpInside) return cell } @objc func printIndexPath(_ sender: UIButton) { let point = sender.convert(CGPoint.zero, to: tableView) guard let indexPath = tableView.indexPathForRow(at: point) else { return } print(indexPath) } }