Wednesday, January 30, 2013

Custom UITableViewCell with UIButton: which button has been clicked? http://stackoverflow.com/questions/9579605/custom-uitableviewcell-with-uibutton-which-button-has-been-clicked

http://stackoverflow.com/questions/9579605/custom-uitableviewcell-with-uibutton-which-button-has-been-clicked

Custom UITableViewCell with UIButton: which button has been clicked?


I'm developing an iOS 4 application with latest SDK and XCode 4.2.

I have a UITableView with sections and with custom UITableViewCell. Every cell has a UIButton and all of these buttons has the same target for UIControlEventTouchUpInside.

This is my code:

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath  {      static NSString* cellIdentifier = @"CalendarCell";        CalendarEventCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];        if (cell == nil)      {          NSArray* topLevelObjects =  [[NSBundle mainBundle] loadNibNamed:@"CalendarEventCell" owner:nil options:nil];            for(id currentObject in topLevelObjects)          {              if ([currentObject isKindOfClass:[CalendarEventCell class]])              {                  cell = (CalendarEventCell *)currentObject;                  [cell.addToCalendarButton addTarget:self action:@selector(addEventToiCal) forControlEvents:UIControlEventTouchUpInside];                  break;              }          }      }  ...  }

When user touch inside that button, how can I know on which section and row was the cell that has been clicked?

share|improve this question
You can able to set tag for that Button. By using that tag number you can able to find out which button is clicked. – Ganesh Mar 6 '12 at 7:42
feedback

Place a tag on the button. For example:

  CalendarEventCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];    if (cell == nil)  {      NSArray* topLevelObjects =  [[NSBundle mainBundle] loadNibNamed:@"CalendarEventCell" owner:nil options:nil];        for(id currentObject in topLevelObjects)      {          if ([currentObject isKindOfClass:[CalendarEventCell class]])          {              cell = (CalendarEventCell *)currentObject;              [cell.addToCalendarButton addTarget:self action:@selector(addEventToiCal) forControlEvents:UIControlEventTouchUpInside];              break;          }      }  }  cell.addToCalendarButton.tag = ((indexPath.section & 0xFFFF) << 16) |                                 (indexPath.row & 0xFFFF);

You will need to change your selector to @selector(addEventToiCal:) and update the method to -(void) addEventToiCal:(UIButton *)sender.

You can then add something like the following to -addEventToiCal:

  if (!([sender isKindOfClass:[UIButton class]]))      return;  NSUInteger section = ((sender.tag >> 16) & 0xFFFF);  NSUInteger row     = (sender.tag & 0xFFFF);  NSLog(@"Button in section %i on row %i was pressed.", section, row);
share|improve this answer
You could also declare the method as - (void) addEventToiCal:(UIButton *)sender and then remove the first line. – Alessandro Vendruscolo Mar 6 '12 at 7:48
@MisterJack I debated writing it that way. Since you seemed to second that idea, I updated the answer to reflect your suggestion. – David M. Syzdek Mar 6 '12 at 7:52
Thanks for your answer. I will need indexPath.section and indexPath.row. – VansFannel Mar 6 '12 at 7:58
Tagging method is easy, but doesn't scale very well, especially when one adds more controls to the cell that have to be identified in the controller and also you need to update the tags once the rows are added and removed. I've seen it cause bugs/problems too many times. – macbirdie Mar 6 '12 at 7:59
@VansFannel You can encode the section and row using something like: tag = ((indexPath.section & 0xFFFF) << 16) | (indexPath.row & 0xFFFF); and then decode them using something like:section = ((tag >> 16) & 0xFFFF); row = (tag & 0xFFFF); – David M. Syzdek Mar 6 '12 at 8:03
show 1 more comment
feedback

Set button's target to a method in the cell instead of setting the target to the controller itself, create a delegate protocol for the cell with a method like tappedButton:(UIButton *)button inCell:(UITableViewCell *)cell and set the controller as cell's delegate. In the target method call that delegate method.

Then in controller's delegate method implementation you can find out cell's NSIndexPath by callingUITableView's tableView:indexPathForCell:.

share|improve this answer
It could be easier to pass section and row to tappedButton: method instead of callingtableView:indexPathForCell:, isn't it? – VansFannel Mar 6 '12 at 8:34
Then you have to hold cell's NSIndexPath as its ivar/property - that's also possible. But you're unnecessarily duplicating data you have to maintain additionally - release it when cell dies, update it when e.g. cells above are added or removed and so on. However if you run into performance problems by runningtableView:indexPathForCell:, which is unlikely, you can use that "shortcut" of course. – macbirdieMar 6 '12 at 8:48
feedback
      Assign Tag value to button like that in cellForRowAtIndexPath method        1-cell.addToCalendarButton.tag=indexPath.row        2-When you add method to button also send the sender so assign method to button like that          [cell.addToCalendarButton addTarget:self action:@selector(addEventToiCal:)forControlEvents:UIControlEventTouchUpInside];          3-In your method read the relevant row like that         -(IBAction)addEventToiCal:(id)sender{        NSLOG("current row is %d",[sender tag]);          }      If you want to now about the section then indexPath do such thing then    - (void)addEventToiCal:(id)sender event:(id)event  {      NSSet *touches = [event allTouches];      UITouch *touch = [touches anyObject];      CGPoint currentTouchPosition = [touch locationInView:self.tableView];      NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];       NsLog("value of indePath.section %d ,indexPath.row %d",indexPath.section,indexPath.row);    }    Assign your method at cellforRowAtIndexPath Like that    [cell.addToCalendarButton addTarget:self action:@selector(addEventToiCal:event:)forControlEvents:UIControlEventTouchUpInside];
share|improve this answer
Thanks for your answer. I will need indexPath.section and indexPath.row. – VansFannel Mar 6 '12 at 7:58
feedback

BNRXIBCell is an excellent solution, for iOS 5 and above. It's a UITableViewCell subclass, intended to be subclassed, to forward action messages from cell subviews to a controller.

share|improve this answer

No comments:

Post a Comment