Rilevare quale UIButton è stato premuto in un UITableView
-
05-07-2019 - |
Domanda
Ho un UITableView
con 5 UITableViewCells
. Ogni cella contiene un UIButton
che è impostato come segue:
- (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;
}
La mia domanda è questa: nel metodo buttonPressedAction:
, come faccio a sapere quale pulsante è stato premuto. Ho preso in considerazione l'uso dei tag ma non sono sicuro che questo sia il percorso migliore. Vorrei poter in qualche modo taggare indexPath
sul controllo.
- (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 è il modo standard di farlo?
Modifica
L'ho risolto un po 'nel modo seguente. Mi piacerebbe ancora avere un'opinione se questo è il modo standard di farlo o esiste un modo migliore?
- (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;
}
La cosa importante da notare è che non posso impostare il tag nella creazione della cella poiché la cella potrebbe invece essere rimossa. Sembra molto sporco. Deve esserci un modo migliore.
Soluzione
Nell'esempio Accessory di Apple viene utilizzato il seguente metodo:
[button addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
Quindi nel gestore di tocco la coordinata del tocco viene recuperata e il percorso dell'indice viene calcolato da quella coordinata:
- (void)checkButtonTapped:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
...
}
}
Altri suggerimenti
Ho trovato il metodo di utilizzo della superview della superview per ottenere un riferimento all'indicePath della cella funzionava perfettamente. Grazie a iphonedevbook.com (macnsmith) per l'informazione testo del link
-(void)buttonPressed:(id)sender {
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...
}
Ecco come lo faccio. Semplice e conciso:
- (IBAction)buttonTappedAction:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero
toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
...
}
Ho trovato una buona soluzione a questo problema altrove, senza problemi con i tag sul pulsante:
- (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...
}
Che ne dici di inviare informazioni come NSIndexPath
nel UIButton
usando il runtime injection.
1) È necessario il runtime sull'importazione
2) aggiungi costante statica
3) aggiungi NSIndexPath
al tuo pulsante in fase di esecuzione utilizzando:
(vuoto) setMetaData: (id) target withObject: (id) newObj
4) alla pressione del pulsante ottieni metadati usando:
(id) Metadati: (id) destinazione
Godetevi
#import <objc/runtime.h>
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
}
Per fare la risposta di (@Vladimir) è Swift:
var buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
var indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)!
Anche se la ricerca di indexPath! = zero
mi dà il dito ... " NSIndexPath non è un sottotipo di NSString "
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")
}
Vorrei usare la proprietà tag come hai detto, impostando il tag in questo modo:
[button setTag:indexPath.row];
quindi ottenere il tag all'interno del pulsantePressedAction in questo modo:
((UIButton *)sender).tag
o
UIButton *button = (UIButton *)sender;
button.tag;
Anche se mi piace il modo dei tag ... se non vuoi usare i tag per qualsiasi motivo,
potresti creare un membro NSArray
di pulsanti premade:
NSArray* buttons ;
quindi crea quei pulsanti prima di eseguire il rendering di tableView e inseriscili nell'array.
Quindi all'interno della tabella tableView: cellForRowAtIndexPath:
puoi fare:
UIButton* button = [buttons objectAtIndex:[indexPath row] ] ;
[cell.contentView addSubview:button];
Quindi nella funzione buttonPressedAction:
, puoi farlo
- (void)buttonPressedAction:(id)sender {
UIButton* button = (UIButton*)sender ;
int row = [buttons indexOfObject:button] ;
// Do magic
}
PER MANEGGIARE LE SEZIONI - Ho archiviato NSIndexPath in un UITableViewCell personalizzato
IN CLKIndexPricesHEADERTableViewCell.xib
IN IB Aggiungi UIButton a XIB - NON aggiungere azione!
Aggiungi outlet @property (conservare, non anatomico) IBOutlet UIButton * buttonIndexSectionClose;
NON CTRL + DRAG un'azione in IB (eseguita nel codice seguente)
@interface CLKIndexPricesHEADERTableViewCell : UITableViewCell
...
@property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose;
@property (nonatomic, retain) NSIndexPath * indexPathForCell;
@end
In viewForHeaderInSection (dovrebbe funzionare anche per cellForRow .... ecc se la tabella ha solo una sezione)
- 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];
... usa la sezione per ottenere dati per la tua cella
... compila
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;
}
L'UTENTE preme il pulsante CANC su un'intestazione di sezione e questo chiama
- (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__);
}
}
In questo esempio ho aggiunto un pulsante Elimina, quindi dovrei mostrare UIAlertView per confermarlo
Memorizzo la sezione e digito nel dizionario memorizzando le informazioni sulla sezione in un ivar nel 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; //:)
}
Con Swift 4.2 e iOS 12, puoi scegliere uno dei 5 seguenti esempi completi per risolvere il tuo problema.
# 1. Usando il convertito di UIView
(_: in :)
e UITableView
indexPathForRow (at :)
UIView
(_: in :) import UIKit
private class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
override init(style: UITableViewCell.CellStyle, 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.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: 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: "CustomCell")
}
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: "CustomCell", for: indexPath) as! CustomCell
cell.button.addTarget(self, action: #selector(customCellButtonTapped), for: .touchUpInside)
return cell
}
@objc func customCellButtonTapped(_ sender: UIButton) {
let point = sender.convert(CGPoint.zero, to: tableView)
guard let indexPath = tableView.indexPathForRow(at: point) else { return }
print(indexPath)
}
}
# 2. Usando il convertito di UIView
(_: in :)
e UITableView
indexPathForRow (at :)
(alternativa )
UIView
(_: in :) Questa è un'alternativa all'esempio precedente in cui passiamo zero
al parametro target
in addTarget (_: action: for :)
. In questo modo, se il primo risponditore non implementa l'azione, verrà inviato al risponditore successivo nella catena del risponditore fino a quando non verrà trovata una corretta implementazione.
import UIKit
private class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button.setTitle("Tap", for: .normal)
button.addTarget(nil, action: #selector(TableViewController.customCellButtonTapped), for: .touchUpInside)
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: 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: "CustomCell")
}
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: "CustomCell", for: indexPath) as! CustomCell
return cell
}
@objc func customCellButtonTapped(_ sender: UIButton) {
let point = sender.convert(CGPoint.zero, to: tableView)
guard let indexPath = tableView.indexPathForRow(at: point) else { return }
print(indexPath)
}
}
# 3. Usando indexPath di UITableView
(per :)
e il modello delegato
UITableView
(per :) In questo esempio, impostiamo il controller di visualizzazione come delegato della cella. Quando si tocca il pulsante della cella, viene attivata una chiamata al metodo appropriato del delegato.
import UIKit
protocol CustomCellDelegate: AnyObject {
func customCellButtonTapped(_ customCell: CustomCell)
}
class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
weak var delegate: CustomCellDelegate?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button.setTitle("Tap", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func buttonTapped(sender: UIButton) {
delegate?.customCellButtonTapped(self)
}
}
import UIKit
class TableViewController: UITableViewController, CustomCellDelegate {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}
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: "CustomCell", for: indexPath) as! CustomCell
cell.delegate = self
return cell
}
// MARK: - CustomCellDelegate
func customCellButtonTapped(_ customCell: CustomCell) {
guard let indexPath = tableView.indexPath(for: customCell) else { return }
print(indexPath)
}
}
# 4. Utilizzando indexPath di UITableView
(per :)
e una chiusura per delega
UITableView
(per :) Questa è un'alternativa all'esempio precedente in cui utilizziamo una chiusura anziché una dichiarazione delegata dal protocollo per gestire il tocco dei pulsanti.
import UIKit
class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
var buttontappedClosure: ((CustomCell) -> Void)?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button.setTitle("Tap", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func buttonTapped(sender: UIButton) {
buttontappedClosure?(self)
}
}
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}
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: "CustomCell", for: indexPath) as! CustomCell
cell.buttontappedClosure = { [weak tableView] cell in
guard let indexPath = tableView?.indexPath(for: cell) else { return }
print(indexPath)
}
return cell
}
}
# 5. Usando accessoriesType di UITableViewCell
e di UITableViewDelegate
tableView(_:accessoryButtonTappedForRowWith:)
di UITableViewCell
e di UITableViewDelegate
tableView(_:accessoryButtonTappedForRowWith:)
Se il tuo pulsante è un controllo accessorio standard di UITableViewCell
, qualsiasi tocco su di esso attiverà una chiamata alla tabella di
, che consente di ottenere il percorso dell'indice correlato. UITableViewDelegate
(_: accessoriesButtonTappedForRowWith: )
import UIKit
private class CustomCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
accessoryType = .detailButton
}
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: "CustomCell")
}
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: "CustomCell", for: indexPath) as! CustomCell
return cell
}
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
print(indexPath)
}
}
Funziona anche per me, grazie @Cocoanut
Ho trovato il metodo di utilizzo della superview della superview per ottenere un riferimento all'indicePath della cella funzionava perfettamente. Grazie a iphonedevbook.com (macnsmith) per il testo del link suggerimento
-(void)buttonPressed:(id)sender {
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...
}
puoi usare il modello di tag:
- (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)
}
Mi sto perdendo qualcosa? Non puoi semplicemente usare il mittente per identificare il pulsante. Il mittente ti fornirà informazioni come questa:
<UIButton: 0x4b95c10; frame = (246 26; 30 30); opaque = NO; tag = 104; layer = <CALayer: 0x4b95be0>>
Quindi, se vuoi cambiare le proprietà del pulsante, pronuncia l'immagine di sfondo che hai appena detto al mittente:
[sender setBackgroundImage:[UIImage imageNamed:@"new-image.png"] forState:UIControlStateNormal];
Se hai bisogno del tag, il metodo di ACBurk va bene.
// how do I know which button sent this message?
// processing button press for this row requires an indexPath.
In realtà abbastanza semplice:
- (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];
}
Funziona bene per me: P
se si desidera regolare la configurazione dell'azione target, è possibile includere il parametro dell'evento nel metodo, quindi utilizzare i tocchi di quell'evento per risolvere le coordinate del tocco. Le coordinate devono ancora essere risolte nei limiti della vista touch, ma ciò potrebbe sembrare più facile per alcune persone.
crea un array nsmutable e metti tutti i pulsanti in quell'array usando [array addObject: yourButton];
nel metodo premere il pulsante
-
(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
}
}
Una leggera variazione sulla risposta di Cocoanuts (che mi ha aiutato a risolverlo) quando il pulsante era in fondo a una tabella (che ti impedisce di trovare la "cella cliccata":
-(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];
}
Uso sempre i tag.
Devi sottoclassare UITableviewCell
e gestire la pressione del pulsante da lì.
È semplice; crea una cella personalizzata e prendi uno sbocco del pulsante
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = @"identifier";
customCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.yourButton.tag = indexPath.Row;
- (void)buttonPressedAction:(id)sender
cambia ID nel metodo sopra in (UIButton *)
Puoi ottenere il valore di quale pulsante viene toccato facendo sender.tag.
Sottoclasse il pulsante per memorizzare il valore richiesto, magari creare un protocollo (ControlWithData o qualcosa del genere). Imposta il valore quando aggiungi il pulsante alla cella della vista tabella. Nel tuo evento di ritocco, controlla se il mittente obbedisce al protocollo ed estrae i dati. Di solito memorizzo un riferimento all'oggetto reale che viene visualizzato nella cella della vista tabella.
SWIFT 2 AGGIORNAMENTO
Ecco come scoprire quale pulsante è stato toccato + invia dati a un altro ViewController dal indexPath.row
di quel pulsante, dato che presumo che sia il punto per la maggior parte!
@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")
}
Per coloro che stanno usando una classe ViewController e hanno aggiunto un TableView, sto usando un ViewController invece di un TableViewController, quindi ho aggiunto manualmente tableView per accedervi.
Ecco il codice per passare i dati a un altro VC quando si tocca quel pulsante e si passa la cella indexPath.row
@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)
}
Nota che sto usando una cella personalizzata, questo codice funziona perfettamente per me
@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);
}
La soluzione di Chris Schwerdt ma poi in Swift ha funzionato per me:
@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)
}
Questo problema ha due parti:
1) Ottenere il percorso dell'indice di UITableViewCell
che contiene UIButton
Ci sono alcuni suggerimenti come:
-
Aggiornamento del
UIButton
tag
nel metodocellForRowAtIndexPath:
utilizzando il valorerow
del percorso dell'indice. Questa non è una buona soluzione in quanto richiede l'aggiornamento continuo ditag
e non funziona con le viste delle tabelle con più di una sezione. -
Aggiungendo una proprietà
NSIndexPath
alla cella personalizzata e aggiornandola invece deltag
di UIButton
incellForRowAtIndexPath:
metodo. Questo risolve il problema di più sezioni ma non è ancora buono in quanto richiede l'aggiornamento sempre. -
Mantenere un debole riferimento a
UITableView
nella cella personalizzata durante la creazione e utilizzando il metodoindexPathForCell:
per ottenere il percorso dell'indice. Sembra un po 'meglio, non è necessario aggiornare nulla nel metodocellForRowAtIndexPath:
, ma richiede comunque l'impostazione di un riferimento debole quando viene creata la cella personalizzata. -
Utilizzo della proprietà
superView
della cella per ottenere un riferimento alUITableView
principale. Non è necessario aggiungere alcuna proprietà alla cella personalizzata e non è necessario impostare / aggiornare nulla al momento della creazione / successiva. Ma ilsuperView
della cella dipende dai dettagli di implementazione di iOS. Quindi non può essere utilizzato direttamente.
Ma questo può essere ottenuto usando un semplice ciclo, poiché siamo sicuri che la cella in questione debba trovarsi in un UITableView:
UIView* view = self;
while (view && ![view isKindOfClass:UITableView.class])
view = view.superview;
UITableView* parentTableView = (UITableView*)view;
Quindi, questi suggerimenti possono essere combinati in un metodo di cella personalizzato semplice e sicuro per ottenere il percorso dell'indice:
- (NSIndexPath *)indexPath
{
UIView* view = self;
while (view && ![view isKindOfClass:UITableView.class])
view = view.superview;
return [(UITableView*)view indexPathForCell:self];
}
D'ora in poi, questo metodo può essere utilizzato per rilevare quale UIButton
viene premuto.
2) Informare le altre parti sull'evento di pressione dei pulsanti
Dopo aver saputo internamente quale UIButton
viene premuto in quale cella personalizzata con percorso dell'indice esatto, queste informazioni devono essere inviate ad altre parti (molto probabilmente il controller di visualizzazione che gestisce UITableView
). Pertanto, questo evento di clic sui pulsanti può essere gestito in un livello di astrazione e logica simile a didSelectRowAtIndexPath:
del metodo del delegato UITableView.
Due approcci possono essere usati per questo:
a) Delega: la cella personalizzata può avere una proprietà delegato
e può definire un protocollo. Quando si preme il pulsante, esegue i suoi metodi delegati sulla proprietà delegate
. Ma questa proprietà delegate
deve essere impostata per ogni cella personalizzata quando vengono create. In alternativa, la cella personalizzata può scegliere di eseguire i suoi metodi delegati anche sul delegato
della sua tabella padre.
b) Centro di notifica: le celle personalizzate possono definire un nome di notifica personalizzato e pubblicare questa notifica con il percorso dell'indice e le informazioni di visualizzazione della tabella padre fornite nell'oggetto userInfo
. Non è necessario impostare nulla per ogni cella, è sufficiente aggiungere un osservatore per la notifica della cella personalizzata.
Uso una soluzione che include la sottoclasse UIButton
e ho pensato di condividerla qui, codici in Swift:
class ButtonWithIndexPath : UIButton {
var indexPath:IndexPath?
}
Quindi ricorda di aggiornare indexPath in 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
}
Quindi, quando si risponde all'evento del pulsante, è possibile utilizzarlo come
func cellButtonPressed(_ sender:UIButton) {
if sender is ButtonWithIndexPath {
let button = sender as! ButtonWithIndexPath
print(button.indexPath)
}
}