Detecting which UIButton was pressed in a UITableView

前端 未结 26 2909
小蘑菇
小蘑菇 2020-11-22 00:40

I have a UITableView with 5 UITableViewCells. Each cell contains a UIButton which is set up as follows:

- (UITableView         


        
相关标签:
26条回答
  • 2020-11-22 00:52
    // how do I know which button sent this message?
    // processing button press for this row requires an indexPath.
    

    Pretty straightforward actually:

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

    Working well for me :P

    if you want to adjust your target-action setup, you can include the event parameter in the method, and then use the touches of that event to resolve the coordinates of the touch. The coordinates still need to be resolved in the touch view bounds, but that may seem easier for some people.

    0 讨论(0)
  • 2020-11-22 00:52

    create an nsmutable array and put all button in that array usint[array addObject:yourButton];

    in the button press method

    -

     (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
    
    }
    }
    
    0 讨论(0)
  • 2020-11-22 00:54

    I use a solution that subclass UIButton and I thought I should just share it here, codes in Swift:

    class ButtonWithIndexPath : UIButton {
        var indexPath:IndexPath?
    }
    

    Then remember to update it's 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
    }
    

    So when responding to the button's event you can use it like

    func cellButtonPressed(_ sender:UIButton) {
        if sender is ButtonWithIndexPath {
            let button = sender as! ButtonWithIndexPath
            print(button.indexPath)
        }
    }
    
    0 讨论(0)
  • 2020-11-22 00:55

    TO HANDLE SECTIONS - I stored the NSIndexPath in a custom UITableViewCell

    IN CLKIndexPricesHEADERTableViewCell.xib

    IN IB Add UIButton to XIB - DONT add action!

    Add outlet @property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose;

    DO NOT CTRL+DRAG an action in IB(done in code below)

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

    In viewForHeaderInSection (should also work for cellForRow.... etc if you table has only 1 section)

    - 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 the section to get data for your cell

    ...fill it in

       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 presses DELETE Button on a Section header and this calls

    - (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 this example I added a Delete button so should show UIAlertView to confirm it

    I store the section and key into the dictionary storing info about the section in a ivar in the 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);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 00:55

    This problem has two parts:

    1) Getting the index path of UITableViewCell which contains pressed UIButton

    There are some suggestions like:

    • Updating UIButton's tag in cellForRowAtIndexPath: method using index path's row value. This is not an good solution as it requires updating tag continuously and it does not work with table views with more than one section.

    • Adding an NSIndexPath property to custom cell and updating it instead of UIButton's tag in cellForRowAtIndexPath: method. This solves multiple section problem but still not good as it requires updating always.

    • Keeping a weak refence to parent UITableView in the custom cell while creating it and using indexPathForCell: method to get the index path. Seems a little bit better, no need to update anything in cellForRowAtIndexPath: method, but still requires setting a weak reference when the custom cell is created.

    • Using cell's superView property to get a reference to parent UITableView. No need to add any properties to the custom cell, and no need to set/update anything on creation/later. But cell's superView depends on iOS implementation details. So it can not be used directly.

    But this can be achieved using a simple loop, as we are sure the cell in question has to be in a UITableView:

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

    So, these suggestions can be combined into a simple and safe custom cell method for getting the index path:

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

    From now on, this method can be used to detect which UIButton is pressed.

    2) Informing other parties about button press event

    After internally knowing which UIButton is pressed in which custom cell with exact index path, this information needs to be sent to other parties (most probably the view controller handling the UITableView). So, this button click event can be handled in a similar abstraction and logic level to didSelectRowAtIndexPath: method of UITableView delegate.

    Two approaches can be used for this:

    a) Delegation: custom cell can have a delegate property and can define a protocol. When button is pressed it just performs it's delegate methods on it's delegate property. But this delegate property needs to be set for each custom cell when they are created. As an alternative, custom cell can choose to perform its delegate methods on it's parent table view's delegate too.

    b) Notification Center: custom cells can define a custom notification name and post this notification with the index path and parent table view information provided in userInfo object. No need to set anything for each cell, just adding an observer for the custom cell's notification is enough.

    0 讨论(0)
  • 2020-11-22 00:58

    It works for me aswell, Thanks @Cocoanut

    I found the method of using the superview's superview to obtain a reference to the cell's indexPath worked perfectly. Thanks to iphonedevbook.com (macnsmith) for the tip link text

    -(void)buttonPressed:(id)sender {
     UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
     NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
    ...
    
    }
    
    0 讨论(0)
提交回复
热议问题