Обнаружение, какая кнопка UIB была нажата в UITableView - PullRequest
209 голосов
/ 26 ноября 2009

У меня есть UITableView с 5 UITableViewCells. Каждая ячейка содержит UIButton, который настроен следующим образом:

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

Мой вопрос таков: в методе buttonPressedAction: как узнать, какая кнопка была нажата. Я рассмотрел использование тегов, но я не уверен, что это лучший маршрут. Я хотел бы иметь возможность каким-либо образом пометить indexPath на элементе управления.

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

Какой стандартный способ сделать это?

Edit:

Я вроде решил это следующим образом. Я все еще хотел бы иметь мнение, является ли это стандартным способом сделать это или есть лучший способ?

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

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

Ответы [ 26 ]

2 голосов
/ 19 октября 2013
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; //:)
}
2 голосов
/ 11 сентября 2012

ДЛЯ ОБРАЩЕНИЯ С РАЗДЕЛАМИ - я сохранил NSIndexPath в пользовательском UITableViewCell

IN CLKIndexPricesHEADERTableViewCell.xib

IN IB Добавить UIButton в XIB - НЕ добавлять действие!

Добавить выход @property (сохранить, неатомный) IBOutlet UIButton * buttonIndexSectionClose;

НЕ CTRL + DRAG действие в IB (сделано в коде ниже)

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

В viewForHeaderInSection (также должно работать для cellForRow .... и т. Д., Если в вашей таблице только 1 раздел)

- 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];

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

... заполните

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

ПОЛЬЗОВАТЕЛЬ нажимает кнопку УДАЛИТЬ в заголовке раздела, и это вызывает

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

В этом примере я добавил кнопку «Удалить», поэтому для ее подтверждения должен отображаться UIAlertView

Я сохраняю раздел и ключ в словаре, хранящий информацию о разделе в иваре в ВК

- (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);
    }
}
1 голос
/ 07 марта 2011

У меня это работает, спасибо @ Cocoanut

Я обнаружил, что метод использования суперпредставления суперпредставления для получения ссылки на indexPath ячейки работает отлично. Спасибо iphonedevbook.com (macnsmith) за текст ссылки на подсказку

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

}
0 голосов
/ 26 ноября 2009

Я всегда использую теги.

Вам нужно создать подкласс UITableviewCell и обработать нажатие кнопки оттуда.

0 голосов
/ 24 июля 2017

Я использую решение, которое подкласс UIButton, и я подумал, что я должен просто поделиться им здесь, коды в Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

Тогда не забудьте обновить его indexPath в 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
}

Так что, отвечая на событие кнопки, вы можете использовать его как

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
0 голосов
/ 27 октября 2016

Эта проблема состоит из двух частей:

1) Получение индексного пути UITableViewCell, содержащего нажатые UIButton

Есть несколько предложений, таких как:

  • Обновление UIButton tag в методе cellForRowAtIndexPath: с использованием значения пути индекса row. Это не очень хорошее решение, так как требует постоянного обновления tag и не работает с представлениями таблиц с несколькими разделами.

  • Добавление свойства NSIndexPath в пользовательскую ячейку и его обновление вместо UIButton tag в методе cellForRowAtIndexPath:. Это решает проблему с несколькими разделами, но все же не очень хорошо, поскольку требует постоянного обновления.

  • Сохранение слабого ссылки на родительский элемент UITableView в пользовательской ячейке при его создании и использовании метода indexPathForCell: для получения пути индекса. Кажется, немного лучше, нет необходимости обновлять что-либо в методе cellForRowAtIndexPath:, но все же требует установки слабой ссылки при создании пользовательской ячейки.

  • Использование свойства ячейки superView для получения ссылки на родителя UITableView. Нет необходимости добавлять какие-либо свойства в пользовательскую ячейку, и нет необходимости устанавливать / обновлять что-либо при создании / позже. Но ячейка superView зависит от деталей реализации iOS. Так что его нельзя использовать напрямую.

Но этого можно добиться с помощью простого цикла, поскольку мы уверены, что рассматриваемая ячейка должна быть в UITableView:

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

Таким образом, эти предложения можно объединить в простой и безопасный пользовательский метод ячейки для получения пути индекса:

- (NSIndexPath *)indexPath
{
    UIView* view = self;

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

    return [(UITableView*)view indexPathForCell:self];
}

Отныне этот метод можно использовать для определения, какая кнопка UIButton нажата.

2) Информирование других сторон о событии нажатия кнопки

После внутреннего выяснения, какая UIButton нажата в какой пользовательской ячейке с точным путем индекса, эта информация должна быть отправлена ​​другим сторонам (наиболее вероятно, контроллер представления, обрабатывающий UITableView). Таким образом, это событие нажатия кнопки может быть обработано на уровне абстракции и логики, аналогичном didSelectRowAtIndexPath: методу делегата UITableView.

Для этого можно использовать два подхода:

a) Делегирование: настраиваемая ячейка может иметь свойство delegate и может определять протокол. Когда кнопка нажата, она просто выполняет свои методы делегата в своем свойстве delegate. Но это свойство delegate должно быть установлено для каждой пользовательской ячейки при ее создании. В качестве альтернативы, пользовательская ячейка может выбрать выполнение своих методов делегата и для родительского табличного представления delegate.

b) Центр уведомлений: пользовательские ячейки могут определять пользовательское имя уведомления и публиковать это уведомление с указанием пути индекса и представления родительской таблицы, предоставленной в объекте userInfo. Не нужно ничего устанавливать для каждой ячейки, достаточно просто добавить наблюдателя для уведомления пользовательской ячейки.

0 голосов
/ 17 октября 2016

Решение Криса Швердта, но тогда у меня в Swift сработало:

@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)
}
0 голосов
/ 07 ноября 2015

ОБНОВЛЕНИЕ SWIFT 2

Вот как узнать, какая кнопка была нажата + отправить данные другому ViewController из indexPath.row этой кнопки, как я полагаю, в этом суть большинства!

@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")

    }

Для тех, кто использует класс ViewController и добавил tableView, я использую ViewController вместо TableViewController, поэтому я вручную добавил tableView для доступа к нему.

Вот код для передачи данных другому VC при нажатии этой кнопки и прохождении ячейки 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)

}
0 голосов
/ 26 ноября 2009

вы можете использовать шаблон тега:

- (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)
}
0 голосов
/ 06 февраля 2015

Обратите внимание, здесь я использую пользовательскую ячейку, этот код отлично работает для меня

 @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);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...