Objetivo c: práctica recomendada para manejar un evento táctil de button para un button de una UITableViewCell personalizada

¿Cuál es la mejor práctica para manejar un evento de button táctil para un button de un UITableViewCell personalizado?

Mis classs: MyViewController , MyCustomCell

Puedo pensar en tres opciones:

Primera opción: tiene el button como propiedad de MyCustomCell y luego agrega un destino en el file MyViewController .m con MyViewController como destino.

MyViewController .m

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"customCell"; MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; [cell.theButton addTarget:self action:@selector(theButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; } // Configure the cell... [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)theButtonTapped:(UIButton *)sender { MyCustomCell *selectedCell = (MyCustomCell *)sender.superview; if (selectedCell) { NSIndexPath *indexPath = [self.tableView indexPathForCell:selectedCell]; MyModel *selectedModel = [self.model objectAtIndex:indexPath.row]; // do something with the model... } } 

Segunda opción: si la celda personalizada se realizó en IB, MyViewController el propietario del file de nib como MyViewController , aplique el método buttonTapped: en MyViewController y conecte el evento Touch Up Inside del buttonTapped: al buttonTapped: method.

Tercera opción: si la celda personalizada no se realizó en IB, agregue un objective al button en el file MyCustomCell .m con MyCustomCell como destino.
Defina un MyCustomCellDelegate add @property (nonatomic, assign) id<MyCustomCellDelegate> delegate en MyCustomCell y llame a este delegado cuando se pulse el button.
Establezca MyViewController como el delegado de la célula cuando cree celdas e implemente el protocolo MyCustomCellDelegate .

MyCustomCell .h

 @class MyCustomCell; @protocol MyCustomCellDelegate <NSObject> - (void)buttonTappedOnCell:(MyCustomCell *)cell; @end @interface MyCustomCell : UITableViewCell @property (nonatomic, retain) UIButton *theButton; @property (nonatomic, assign) id<MyCustomCellDelegate> delegate; @end 

MyCustomCell .m

 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // Initialization code self.theButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; self.theButton.frame = CGRectMake(10,10,50,30); [self addSubview:self.theButton]; [self.theButton addTarget:self action:@selector(theButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; } return self; } - (void)theButtonTapped:(UIButton *)sender { [self.delegate buttonTappedOnCell:self]; } 

MyViewController .m

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"customCell"; MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.delegate = self; } // Configure the cell... [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)buttonTappedOnCell:(MyCustomCell *)selectedCell { if (selectedCell) { NSIndexPath *indexPath = [self.tableView indexPathForCell:selectedCell]; MyModel *selectedModel = [self.model objectAtIndex:indexPath.row]; // do something with the model... } } 

Almacena la fila de la celda como propiedad de tag de tu button personalizado.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // bla bla bla if (!cell) { //bla bla bla [cell.yourButton addTarget:self selector:@selector(yourButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; } // bla bla bla cell.yourButton.tag = indexPath.row; } -(void)yourButtonTapped:(id)sender { int tag = [(UIButton *)sender tag]; NSLog(@"tapped button in cell at row %i", tag); } 

Usar la label, desde mi punto de vista, rompería la rigurosidad de su código. Además, cuando tiene varias secciones, usar la label definitivamente haría un lío de su código.

Para evitar este problema, puede subclass UITableViewCell y hacer que mantenga una propiedad indexPath para que la célula sepa su position precisa.

Otro problema aquí es que, si UITableView invoca API para insert o delete fila, debe actualizar los datos de position de las celdas visibles

No creo que esa sea la mejor práctica.

Existe una mejor manera.


Recomiendo encarecidamente usar MVVM cuando tenga que manejar diferentes events táctiles en su Celda.

En este patrón, tu UITableViewCell personalizado tendría un CellViewModel personalizado. Esta class sería responsable de mantener todos los datos que asocia con la celda, para que pueda recuperar los datos y poner la lógica de event handling events dentro de la celda.

He implementado un enfoque basado en bloques subclassando UIButton:

 typedef void (^ActionBlock)(id sender); @interface UIBlockButton : UIButton { ActionBlock _actionBlock; } -(void)handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action; ​@end @implementation UIBlockButton -(void) handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action { _actionBlock = action; [self addTarget:self action:@selector(callActionBlock:) forControlEvents:event]; } -(void) callActionBlock:(id)sender{ _actionBlock(sender); } @end 

Y en tableview delegado:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (!cell) { cell.yourButton.tag = indexPath.row;// pass tag [cell.yourButton handleControlEvent:UIControlEventTouchUpInside withBlock:^(id sender) { // your selector action code NSLog(@"tapped button in cell at row %i",[(UIButton *)sender tag]); }]; } } 

En algún momento, su button se toca, y en ese punto es una subvista de una celda que es una subvista de alguna vista de tabla.

Simplemente escriba un método que tome una vista, suba la cadena de supervisión para encontrar la celda contenedora, vaya más arriba para encontrar la vista de tabla y luego solicite la vista de tabla para la ruta de índice de la celda.

Es mucho más fácil y más confiable que almacenar una label que contenga una fila, ya que no se encuentra con problemas cuando se edita la vista de tabla, y es mucho mejor tener un código que descubra qué path de índice es cuando necesita la ruta de índice y no en algún código completamente no relacionado cuando se crea la celda.

Solución Swift 3.0

 cell.btnRequest.tag = indexPath.row cell.btnRequest.addTarget(self,action:#selector(buttonClicked(sender:)), for: .touchUpInside) func buttonClicked(sender:UIButton) { let buttonRow = sender.tag }