简体   繁体   中英

Custom UITableViewCell button action?

I've been looking around to find a solution to this, but can't seem to find one that works for me. I have a custom cell with a button inside. My problem is how do I pass the indexPath to the action method?

Right now I'm doing

 [cell.showRewards addTarget:self action:@selector(myAction:) forControlEvents:UIControlEventTouchUpInside];

In my cellForRowAtIndexPath method and my method is:

-(IBAction)myAction:(id)sender{
NSIndexPath *indexPath = [self.tableView indexPathForCell:(MyCustomCell *)[sender superview]];
NSLog(@"Selected row is: %d",indexPath.row);
}

Any tips? Thanks.

Just want to add what I believe is the best solution of all: a category on UIView.

It's as simple as this:

- (void)somethingHappened:(id)sender
{
    NSIndexPath *indexPath = [self.tableView indexPathForCell:[sender parentCell]];

    // Your code here...
}

Just use this category on UIView:

@interface UIView (ParentCell)

- (UITableViewCell *)parentCell;

@end

@implementation UIView (ParentCell)

- (UITableViewCell *)parentCell
{
    UIView *superview = self.superview;
    while( superview != nil ) {
        if( [superview isKindOfClass:[UITableViewCell class]] )
            return (UITableViewCell *)superview;

        superview = superview.superview;
    }

    return nil;
}

@end
cell.showRewards.tag = indexPath.row;

-(IBAction)myAction:(id)sender
{
UIButton *btn = (UIButton *)sender;
int indexrow = btn.tag;
NSLog(@"Selected row is: %d",indexrow);
}

While I feel setting tag for the button is one way to go. You might need to write code to make sure each time the cell gets reused, the appropriate tag gets updated on the button object.

Instead I have a feeling this could work. Try this -

-(IBAction)myAction:(id)sender
{
CGPoint location            = [sender locationInView:self.tableView];
NSIndexPath *indexPath      = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *swipeCell  = [self.tableView cellForRowAtIndexPath:indexPath];

NSLog(@"Selected row: %d", indexPath.row);
//......
}

Essentially what you are doing is getting the coordinates of where the click happened with respect to your tableView . After getting the coordinates, tableView can give you the indexPath by using the method indexPathForRowAtPoint: . You are good to go after this...

Voila, you have not just the indexPath but also the actual cell where the click happened. To get the actual data from your datasource (assuming its NSArray), you can do -

[datasource objectAtIndex:[indexPath row]];

Try this one.

cell.showRewards.tag=indextPath.row

implement this in cellforrowatindexpath tableview's method.

-(IBAction)myAction:(id)sender{
UIButton* btn=(UIButton*)sender;
NSLog(@"Selected row is: %d",btn.tag);
}

您设置按钮标记值= indexpath并在函数中检查它,如果标记值是这样做你想要的

You can assign indexpath to button tag and access in your method like

cell.showRewards.tag = indexPath.row;

-(IBAction)myAction:(id)sender
{
     NSIndexPath *indexPath = [self.tableView indexPathForCell:[sender tag]];
     NSLog(@"Selected row is: %d",indexPath.row);
}

In custom UITableViewCell class:

[self.contentView addSubview:but_you]; 

In cellForRowAtIndexPath method you can write:

[cell.showRewards addTarget:self action:@selector(myAction:) forControlEvents:UIControlEventTouchUpInside];    
cell.showRewards.tag = indexPath.row;

I find it incredible that there isn't really a decent solution to this.

For whatever reason, I find the tagging methods and the 'using the visual location of the cell on the screen to identify the correct model object' outlined in the other answers a bit dirty.

Here are two different approaches to the problem:

Subclassing UITableViewCell

The solution I went with was to sub class UITableViewCell

@interface MyCustomCell : UITableViewCell

@property (nonatomic, strong) Model *myModelObject;

@end

When creating the cell in cellForRowAtIndexPath: you are likely to be using the model object to populate the cell data. In this method you can assign the model object to the cell.

And then in the button tap handler:

MatchTile *cell = (MatchTile *) sender.superview.superview;

if (cell && cell.myModelObject)
{
    //Use cell.myModelObject
}

I'm not 100% happy with this solution to be honest. Attaching domain object to such a specialised UIKit component feels like bad practice.

Use Objective-C Associative Objects

If you don't want to subclass the cell there is a another bit of trickery you can use to associate the model object with the cell and retrieve it later.

To retrieve the model object from the cell, you will need a unique key to identify it. Define one like this:

static char* OBJECT_KEY = "uniqueRetrievalKey"; 

Add the following line to your cellForRowAtIndexPath: method when you are using the model object to populate the cell. This will associate your model object with the cell object.

objc_setAssociatedObject(cell, OBJECT_KEY, myModelObject, OBJC_ASSOCIATION_RETAIN);

And then anywhere you have a reference to that cell you can retrieve the model object using:

MyModelObject *myModelObject = (MyModelObject *) objc_getAssociatedObject(cell, OBJECT_KEY);

In reflection, although I opted for the first (because I'd already subclassed the cell), the second solution is probably a bit cleaner since it remains the responsibility of the ViewController to attach and retrieve the model object. The UITableViewCell doesn't need to know anything about it.

In - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method write the code below:

  [cell.zoomButton addTarget:self action:@selector(navigateAction:) forControlEvents:UIControlEventTouchUpInside];
cell.zoomButton.tag=indexPath.row;

Then write a method like this:

-(IBAction)navigateAction:(id)sender
{
UIButton *btn = (UIButton *)sender;
int indexrow = btn.tag;
NSLog(@"Selected row is: %d",indexrow);

currentBook = [[bookListParser bookListArray] objectAtIndex:indexrow];

KitapDetayViewController *kitapDetayViewController;
if(IS_IPHONE_5)
{
    kitapDetayViewController = [[KitapDetayViewController alloc] initWithNibName:@"KitapDetayViewController" bundle:Nil];
}

else
{
    kitapDetayViewController = [[KitapDetayViewController alloc] initWithNibName:@"KitapDetayViewController_iPhone4" bundle:Nil];
}
kitapDetayViewController.detailImageUrl = currentBook.detailImageUrl;
kitapDetayViewController.bookAuthor = currentBook.bookAuthor;
kitapDetayViewController.bookName = currentBook.bookName;
kitapDetayViewController.bookDescription = currentBook.bookDescription;
kitapDetayViewController.bookNarrator=currentBook.bookNarrator;
kitapDetayViewController.bookOrderHistory=currentBook.bookOrderDate;
int byte=[currentBook.bookSizeAtByte intValue];
int mb=byte/(1024*1024);
NSString *mbString = [NSString stringWithFormat:@"%d", mb];
kitapDetayViewController.bookSize=mbString;
kitapDetayViewController.bookOrderPrice=currentBook.priceAsText;
kitapDetayViewController.bookDuration=currentBook.bookDuration;
kitapDetayViewController.chapterNameListArray=self.chapterNameListArray;
// [[bookListParser bookListArray ]release];
[self.navigationController pushViewController:kitapDetayViewController animated:YES];
}

If you want the indexPath of the button Detecting which UIButton was pressed in a UITableView describe how to.

basically the button action becomes:

 - (void)checkButtonTapped:(id)sender
    {
        CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
        if (indexPath != nil)
        {
         ...
        }
    }

In [sender superview] you access not MyCustomCell , but it's contentView . Read UITableViewCell Class Reference:

contentView Returns the content view of the cell object. (read-only)

@property(nonatomic, readonly, retain) UIView *contentView

Discussion: The content view of a UITableViewCell object is the default superview for content displayed by the cell. If you want to customize cells by simply adding additional views, you should add them to the content view so they will be positioned appropriately as the cell transitions into and out of editing mode.

Easiest way to modify your code is to use [[sender superview] superview] . But this will stop working if you later modify your cell and insert button in another view. contentView appeared in iPhoneOS 2.0 . Similar future modification will influence your code. That the reason why I don't suggest to use this way.

这是“最简单的方法”(在IOS11上测试):

NSIndexPath *indexPath = [myTable indexPathForRowAtPoint:[[[sender superview] superview] center]];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM