I have my custom uitableviewcell. It has the uibutton, uibutton with action. So how do I know indexPath of the row with pressed uibutton??
Target: uibuttons on each rows has an image. So when user taps the button it shows the alertview and if answer is "yes" uibutton's image changes.
Try below code snippet:
In cellForRowAtIndexPath method. set indexpath.row as button tag
[cellButton addTarget:self action:#selector(cellBottonClicked:)
forControlEvents:UIControlEventTouchUpInside];
cellButton.tag = indexPath.row;
-(IBAction) cellBottonClicked:(id)sender{
NSInteger row = [sender tag];
//you can get indexpath from row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
CustomTableViewCell *cell = (CustomTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
// from this cell you can fetch and change image of selected cell's button.
}
[EDIT : Change Only image of Button]
If you only required to change Image of Button on click event.then I think, you do not need for indexpath or cell.
try this code. Hope it will helps:
-(IBAction) cellBottonClicked:(id)sender{
UIButton *btn = (UIButton *)sender;
[btn setImage:yourImage forState:UIControlStateNormal];
}
You should also be able to get the indexPath by:
NSIndexPath *indexPathForButton = [self.tableView indexPathForSelectedRow];
or in your .h file, add:
#property (nonatomic, retain) NSIndexPath* indexPathForButton;
and change the indexPath, whenever it's used. (self.indexPathForButton = indexPath;)
But using tags is much comfortable…
You can assign the row of the current cell as the tag of the UIButton. On your target/action, you can check for the tag of the button to find the row in which it was pressed.
Related
In my app I am facing with a really weird problem. I am trying to solve it for hours but could not find what is the problem so far.
Well, I have a tableview with custom cells. and each cell I have a button. It is is like an empty box. When the button pressed, I want to change the related cell image of that button. Actually I successfully do that but somehow, some other cell's button images are also changing and I could not figure out why. Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
//create new cell
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[cell.tickButton setTag:indexPath.row];
[cell.tickButton addTarget:self action:#selector(buttonWasPressed:)forControlEvents:UIControlEventTouchUpInside];
//cell has 2 label so I filled them with the contents of my object array that part is working with no problem
cell.leftLabel.text = [NSString stringWithFormat:#"%#", [[contacts objectAtIndex:indexPath.row]name]];
cell.rightLabel.text = [NSString stringWithFormat:#"%#", [[contacts objectAtIndex:indexPath.row]email]];
return cell;
}
in my buttonWasPressed method I simply do:
-(IBAction)buttonWasPressed:(id)sender
{
UIButton *button = (UIButton *)sender;
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateNormal];
}
As I said before it works but also changes some other buttons images and also when I scroll down and get back to the initial position I see that some button images are changed.
You can access that cell and can change the button image as :
-(IBAction)buttonWasPressed:(id)sender
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:your_row inSection:your_section];
UITableViewCell *currentCell = [table cellForRowAtIndexPath:indexPath];
UIButton *button = (UIButton *)[currentCell viewWithTag:(UIButton *)sender.tag];
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateNormal];
}
Hope it helps you.
You can deal with touch within CustomCell class. Just move method -(IBAction)buttonWasPressed:(id)sender within your custom class and add
tickButton addTarget:selfaction:#selector(buttonWasPressed:)forControlEvents:UIControlEventTouchUpInside in init method.
The problem is calling
[cell.tickButton addTarget:self action:#selector(buttonWasPressed:)forControlEvents:UIControlEventTouchUpInside];
method.
You add target everytime when your cells are reused and remove never. So your buttons have many targets and action method get called multiple times respectively. It may cause a memory leak.
Just put the addTarget method in if clause.
It will solve your one problem, which is your method get called multiple times. But when the cell is reused the button has the changed image. You need to set it back.
The best way to do that, you add a property to the contact object which are preseted in cells and set a value when the action method is fired. Then in cellForRowAtIndexPath: method you can set the image of the button by checking that property.
Do this
NSString *CellIdentifier = [NSString stringWithFormat:#"cell%d",indexPath.row];
While you set the default image to button also do this
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateSelected];
In your button action method do this:
-(IBAction)buttonWasPressed:(id)sender
{
UIButton *button = (UIButton *)sender;
if(button.selected==NO) {
[button setSelected:YES];
}
}
Hope this helps.
To reuse your cell, you have to make it clean after dequeueing. In this case, you should reset your image first:
[cell.tickButton setImage:nil forState:UIControlStateNormal];
Before assigning anything to your cell.
You must save button's tag which was pressed than try this
if(pressed[cell.tickButton.tag])
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateNormal];
else
[cell.tickButton setImage:nil forState:UIControlStateNormal];
I guess you are giving the default image to your button inside the CustomCell class. This problem is very common in UITableviews.
All you need to do is keep track of the cells for which the button has been pressed. Do all this inside the cellForRowAtIndexPath method.
For example:
update the code inside cellForRowAtIndexPath method
if (cell == nil)
{
//create new cell
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(yourCondition==YES){
change the button image to the special image.
}
else{
keep the button image as default image.
}
return cell;
To keep track of the cells you can store the index path of the cells(whose button has been tapped) in an array. Inside cellForRowAtIndexPath check if the current indexPath is present inside your array or not
I have UIButton which is placed on top of each UITableviewCell . It is custom tableview . When i click on tableviewcell it goes into another tableview where (Tableview) contains the data in each cell .
This is the way i get the details of the 2nd tableview ....customCellData is the db query method which gives out the cell number ..(Ex if i click 2nd cell of first tableview customcell returns the value )
(bookArray - contains the list of items present in 2nd cell of first tableviewcell)
The following code is didSelectForRowAtIndexPath
NSDictionary *selectedAuthor = nil;
NSArray *sectionArray=[mainIndexDictionary objectForKey:[allKeysArray objectAtIndex:indexPath.section]];
selectedAuthor = [sectionArray objectAtIndex:indexPath.row];
iPath=[[selectedAuthor objectForKey:#"SymptIndexID"] intValue];
NSLog(#"iPath - %d",iPath);
authortitleString=[[selectedAuthor objectForKey:#"authorIndexName"]stringByAppendingString:#" cure!"];
}
bookArray=[objDB customCellData:(NSInteger)iPath];
[self.view bringSubviewToFront:authorView];
[bookTableView scrollRectToVisible:CGRectMake(0.0, 0.0, 320.0,10.0) animated:NO];
[bookTableView reloadData]
Now i want to program in such a way where .when i click on the UIButton which is there in 1st tableview it should contain the data of the 2nd tableview . I cant access indexPath since i will be doing this in seperate method ( In didselectrowatindexpath if i give indexpath for that particular cell the indexpath is found )
Will anyone please help me .
Updated Code
in cellForRowAtIndexPath
cell.shareFacebookButton.tag = indexPath.row;
[cell.shareFacebookButton addTarget:self action:#selector(facebookShare:) forControlEvents:UIControlEventTouchUpInside];
return cell;
-(IBAction)facebookShare:(id)sender
{
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
UITableView *tableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
NSInteger indexPathNumber = indexPath.row;
NSLog(#"IndexPath is -- > %d" , indexPathNumber);
}
Answer Updated
Ok, Below is the function you need to use for custom cell buttons
"cellForRowAtIndexPath" or use any other given appropriate function.
And to identify which button was clicked, do as below
<buttonid>.tag = <indexPath>.row;
Regards,
Ravi
I have a button inside every cell of my UITableView, but when the button is triggered, I want to find out which cell it's inside. I could use a tag, or subclass the button and add a indexPath to it, but then the problem comes when I'm deleting or adding cells and I have to keep up with updating all those buttons. Is there another way people can think of doing this well?
UITableViewCell * cell = (UITableViewCell*) button.superview;
NSIndexPath * indexpath = [myTableView indexPathForCell:cell]
Use UITableView's indexPathForCell: method. It takes a cell, and gives you the index path for the cell.
try this
- (void) buttonAction:(id)sender{
UIButton *buttonInCell = (UIButton *)sender;
NSIndexPath *indexPathOfCell = [self.yourTable indexPathForCell:
(UITableViewCell *)[[buttonInCell superview] superview]];
}
where the buttonInCell hold the sender Button and indexPathOfCell holds the indexPath value of the Cell that has the Particular Button from which the action has been triggered
the above way may not work with iOS 7
update
- (void)buttonAction:(id)sender {
UIButton *buttonInCell = (UIButton *)sender;
CGPoint center= buttonInCell.center;
CGPoint rootViewPoint = [buttonInCell.superview convertPoint:center toView:self.yourTableView];
NSIndexPath *indexPath = [self.yourTableView indexPathForRowAtPoint:rootViewPoint];
NSLog(#"%#",indexPath);
}
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.
cell.showRewards.tag = indexPath.row;
-(IBAction)myAction:(id)sender
{
UIButton *btn = (UIButton *)sender;
int indexrow = btn.tag;
NSLog(#"Selected row is: %d",indexrow);
}
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
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);
}
You set the button tag value = indexpath and check it in function if tag value is this do what u want
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;
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);
}
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 [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.
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)
{
...
}
}
Here is the "Simplest Way" to do it (Tested on IOS11):
NSIndexPath *indexPath = [myTable indexPathForRowAtPoint:[[[sender superview] superview] center]];
I use UITableview, using IB I defined a UIButton in a custom cell and some labels, The custom cell subclass already have definitions of IBAction of the button and necessary IBOutlets on it, but I want to handle the button click events in the tableview controller it self but not in the custom cell subclass.
How can I do this? also I need to get which row's button is exactly clicked so I will show the content relavant to it.
I solved problem by addding this into my controller;
[cell.expButton setTag:indexPath.row];
UIButton *button = (UIButton *)[cell expButton];
[button addTarget:self action:#selector(buttonTapped:event:) forControlEvents:UIControlEventTouchUpInside];
For the need of clickable event(touch effect on the subView), I usually do this :
-(void)viewDidLoad{
self.tableView.delaysContentTouches = NO;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// This will do the touch down effect on the subView of your UIButton
for (id obj in cell.subviews)
{
if ([NSStringFromClass([obj class]) isEqualToString:#"UITableViewCellScrollView"])
{
UIScrollView *scroll = (UIScrollView *) obj;
scroll.delaysContentTouches = NO;
break;
}
}
// I need to get which row's button is exactly clicked so I will show the content relavant to it.- here it is
UIButton *btn = (UIButton*)[cell viewWithTag:200];
// btn = cell.myButton; If you are connected with the IBOutlet
[btn setTag:200 + indexPath.row];
[self.btnPlay addTarget:self action:#selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)btnAction: (id)sender{
UIButton *btn = (UIButton *)sender;
NSLog(#"Selected subView of the Row is %d th tag", btn.tag-200);
// You can get the UITableViewCell using the tag property
selectedIP = [NSIndexPath indexPathForRow:btn.tag - 200 inSection:1;
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedIP];
// Do the relevant code
}
the way how you want to do this is right (how MVC have to coded clean) - thats beautiful!
To make it more efficent you have to use delegation - thats the best way how to do this!
Here a way how you can do this (e.g. in swift).
https://stackoverflow.com/a/29920564/1415713