Цель c - Лучшая практика для обработки события нажатия кнопки для кнопки пользовательского UITableViewCell

Что лучше всего обрабатывать событие нажатия кнопки для кнопки пользовательского UITableViewCell?

мои классы: MyViewController, MyCustomCell

Я могу представить три варианта:

Первый параметр -. У кнопки есть свойство MyCustomCell, а затем добавьте в него объект в файле MyViewController.m с MyViewController в качестве цели.

MyViewController.m file

- (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...
    }
}

Второй вариант -. Если пользовательская ячейка была сделана в IB, установите для файла nib File Owner значение MyViewController, внесите buttonTapped: метод в MyViewController и подключите кнопку Touch Up Inside event к методу buttonTapped:.

Третий вариант -, если пользовательская ячейка не была сделана в IB, добавьте цель в файл MyCustomCell.m с MyCustomCell в качестве цели.
Определите MyCustomCellDelegate добавьте @property (nonatomic, assign) id<MyCustomCellDelegate> delegate в MyCustomCell и вызовите этот делегат при нажатии кнопки.
Установите MyViewController в качестве делегата ячейки при создании ячеек и реализуйте протокол 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 file

- (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 file

- (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...
    }
}

Ответы

Ответ 1

Сохраните строку ячейки как свойство tag вашей пользовательской кнопки.

- (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);
}

Ответ 2

Использование тега, с моей точки зрения, нарушит строгость вашего кода. Кроме того, если у вас несколько разделов, использование тега определенно вызовет беспорядок вашего кода.

Чтобы избежать этой проблемы, вы можете подклассифицировать UITableViewCell и удержать свойство indexPath, чтобы ячейка могла узнать его точное положение.

Другая проблема заключается в том, что если UITableView вызывает API до строки insert или delete, вам необходимо обновить данные позиции видимых ячеек

Я не думаю, что это лучшая практика.

Существует лучший способ.


Я настоятельно рекомендую использовать MVVM, когда вам приходится обрабатывать различные события касания в своей ячейке.

В этом шаблоне пользовательский UITableViewCell будет содержать пользовательский CellViewModel. Этот класс будет отвечать за хранение всех данных, которые вы связываете с ячейкой, чтобы вы могли извлекать данные и поместить логику обработки событий внутри ячейки.

Ответ 3

Я реализовал основанный на блоках подход путем подкласса 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

И в делегате tableview:

- (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]);
     }];
   }  
}

Ответ 4

В какой-то момент ваша кнопка будет прослушиваться, и в этот момент она представляет собой подпункт ячейки, которая является подзором некоторого вида таблицы.

Просто напишите метод, который принимает представление, поднимается цепочка супервизора, чтобы найти содержащую ячейку, идет дальше, чтобы найти табличное представление, а затем запрашивает табличное представление для indexPath ячейки.

Это намного проще и надежнее, чем хранить тег, содержащий строку, потому что вы не сталкиваетесь с проблемами при редактировании tableview, и гораздо лучше иметь код, который обнаруживает, какой indexPath он есть, когда вам нужно indexPath, а не в некотором полностью несвязанном коде при создании ячейки.

Ответ 5

Решение 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
 }