How to get the indexPath of a UIButton in a UITableViewCell? [duplicate] - ios

This question already has answers here:
How to know the UITableview row number
(10 answers)
Closed 5 years ago.
I have a UITableview with multiple cells. On each cell I'm adding buttons dynamically according to some array count. So, when I click on a button I'm able to get the tag value of the button. But how to get the indexPath of that cell?
Here is my code in -cellForRowAtIndexPath:
UIView *view=(UIView*)[cell.contentView viewWithTag:indexPath.row+444];
UIImageView *img=(UIImageView*)[cell.contentView viewWithTag:indexPath.row+999];
img.image=[UIImage imageNamed:#"BHCS_empty.png"];
if(integer!=50)
{
NSInteger y_axis=0;
NSArray *Arr=[tableSubCategoryArr objectAtIndex:indexPath.row];
img.image=[UIImage imageNamed:#"BHCS_selected.png"];
view.Frame= CGRectMake(0,50,281, integer-50);
for (int i=0; i<[Arr count]; i++)
{
NSLog(#"arr %#",[Arr objectAtIndex:i]);
UIButton *Btn=[UIButton buttonWithType: UIButtonTypeCustom];
Btn.frame=CGRectMake(0, y_axis, 281, 44);
[Btn setImage:[UIImage imageNamed:#"BHCS_panel.png"] forState:UIControlStateNormal];
[Btn addTarget:self action:#selector(subCategoryBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
[Btn setTag:i+100];
[view addSubview:Btn];
UILabel *nameLbl=[[UILabel alloc]initWithFrame:CGRectMake(20, y_axis,248, 44)];
nameLbl.text = [[Arr objectAtIndex:i]objectForKey:#"SubCategoryName"];
nameLbl.textColor = [UIColor whiteColor];
nameLbl.backgroundColor=[UIColor clearColor];
panelTableView.separatorColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"BHCS_panel_div1.png"]];
nameLbl.font = [UIFont fontWithName:#"Helvetica" size:12.0f];
[view addSubview:nameLbl];
y_axis=y_axis+44+1.3f;
}
}

I have tried maximum of given answers, but at the and I generally use to go for most Generalised and ideal way as follows:
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];

ALLLLL of the answers here are bad and you shouldn't be looping through superviews. Classic example, with iOS 7 Apple changed the tableViewCell hierarchy and your app will now crash!
Use this instead:
How to know the UITableview row number

Updated answer
Use it like:
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];

Thanks to all i slove this by using below code
NSIndexPath *indexPath = [panelTableView indexPathForCell:(UITableViewCell *)sender.superview.superview];
NSLog(#"%d",indexPath.row);

None of these answers seem like a very clean solution. The way I would implement this is by using the delegate pattern. The view controller is the cell's delegate, meaning you let the cell itself handle the button press, and it tells its delegate when the button was pressed so it can handle it however it wants.
Let's say you have a tableview where each cell represents a Person object, and when the button is pressed you want to show a profile for this person. All you need to do is this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PersonCell *cell = [tableView dequeueReusableCellWithIdentifier:#"customTableViewCell" forIndexPath:indexPath];
cell.delegate = self;
cell.person = self.people[indexPath.row];
return cell;
}
- (void)personCell:(PersonCell *)personCell didPressButtonForPerson:(Person *)person {
[self showProfileForPerson:person];
}
Then all you need to do in your button class is add a property called buttonPressedHandler that is a block passing back an instance of Person, and when you create your button and add the target do something like this:
- (void)viewDidLoad {
[super viewDidLoad];
// do whatever whatever else you need/want to here
[self.button addTarget:self selector:#selector(handleButtonPressed) forState:UIControlStateNormal];
}
- (void)handleButtonPressed {
// Make sure this is a required delegate method, or you check that your delegate responds to this selector first...
[self.delegate personCell:self didPressButtonForPerson:self.person];
}

Put this code in the button's action method:
NSIndexPath *indexPath = [yourtableviewname indexPathForCell:(UITableViewCell *)sender.superview];
NSLog(#"%d",indexPath.row);

I suggest adding a property to your custom UITableViewCell implementation class to store the indexPath. Then, when your cellForRowAtIndexPath delegate fires in your TableViewController, set the indexPath property for that cell.
customTableViewCell.h :
#interface customTableViewCell : UITableViewCell
#property NSIndexPath *indexPath;
#end
customTableViewController configure cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
customTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"customTableViewCell" forIndexPath:indexPath];
// Do whatever you want with your cell
cell.indexPath = indexPath;
return cell;
}
Then you can refer to the indexPath property in your customTableViewCell by calling self.indexPath

You can use UITableView's indexPathForCell: method like so [tableView indexPathForCell:cell];. Good Luck!

Related

UIButton in UITableView clicks on multiple buttons in objective c

I have 2 buttons in UITableview. When i select one button in my UITableView other buttons lower down are also clicked. Why is this clicking one button selecting multiple UIButtons?
I am using storyboard and my code is:
- (IBAction)yesBtnAction:(id)sender {
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *indexPath = [self.take5table indexPathForCell:clickedCell];
UIButton *yesBTN=(UIButton *)[clickedCell.contentView viewWithTag:100];
UIButton *noBTN=(UIButton *)[clickedCell.contentView viewWithTag:111];
[yesBTN setBackgroundColor:[UIColor colorWithRed:0.23 green:0.62 blue:0.23 alpha:1.0]];
[noBTN setBackgroundColor:[UIColor whiteColor]];
}
- (IBAction)noBtnAction:(id)sender {
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *indexPath = [self.take5table indexPathForCell:clickedCell];
UIButton *yesBTN=(UIButton *)[clickedCell.contentView viewWithTag:100];
UIButton *noBTN=(UIButton *)[clickedCell.contentView viewWithTag:111];
[yesBTN setBackgroundColor:[UIColor whiteColor]];
[noBTN setBackgroundColor:[UIColor redColor]];
}
Any help/suggestions would be greatly appreciated.
The problem is that you are not properly handling cell reuse.
You need to make sure your cellForRowAtIndexPath properly sets the state of each button. This needs to be based on some state you keep track of in your data model. This data model needs to be updated as each button it clicked.
You are attempting to keep the state in the button. That doesn't work.
I don't think its because of the same function being called.
Your problem is owing to dequeuing of the cells. Example:
In the first cell, when you click a button, you are changing the yesBTN to another color. So when the cell is re-used you are using that same cell with the changed color. What you could try is:
Add the indexPath.row of all the buttons clicked to a NSMutableArray and in your cellForRowAtIndexPath check if the array contains that particular indexPath.row
I think this code is helpful for u
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *Cell = [tableView dequeueReusableCellWithIdentifier:#"" forIndexPath:indexPath];
[cell.buttnName1 addTarget:self action:#selector(BtnAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.buttnName2 addTarget:self action:#selector(BtnAction:) forControlEvents:UIControlEventTouchUpInside];
return Cell;
}
//action for button
- (void)BtnAction:(id)sender//type 1 button action
{
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
UIButton *button = sender;
for (button in clickedCell.ButtonArr)//button create IBOtletcollections
{
[button setSelected:([button isEqual:sender])?YES:NO];
if ([button isSelected]) {
[button setBackgroundImage:[UIImage imageNamed:#"select_toggle"] forState:UIControlStateSelected];
tagType = button.tag;
//use local variable Tagtype(NsIntiger)
}else{
[button setBackgroundImage:[UIImage imageNamed:#"unselect_toggle"] forState:UIControlStateNormal];
}
}
}

How can I create a 30 row UITableView, without doing it statically, and accept UITextField input on each row?

Normally, if I wanted to have a UITextField as a part of a UITableViewCell, I would likely use either a) static rows or b) I would create the cell in the storyboard, outlet the cell and outlet the field to my ViewController, and then drag the cell outside of the "Table View", but keeping it in the scene.
However, I need to create a View where I accept input from 28 various things. I don't want to outlet up 28 different UITextField's.
I want to do this dynamically, to make it easier. So I've created a custom UITableViewCell with a Label and UITextField.
My ViewController has two arrays.
#property (nonatomic, strong) NSArray *items;
#property (nonatomic, strong) NSArray *itemValues;
My cellForRowAtIndexPath looks something like this...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = #"ItemCell";
MyItemTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[MyItemTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
[cell.itemValue addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
} else {
if (![cell.itemValue actionsForTarget:self forControlEvent:UIControlEventEditingChanged]) {
[cell.itemValue addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
}
cell.item.text = [self.items objectAtIndex:indexPath.row];
cell.itemValue.text = [self.itemValues objectAtIndex:indexPath.row];
return cell;
}
- (void)textFieldDidChange:(id)sender
{
NSLog(#"textFieldDidChange: %zd", [self.tableView indexPathForSelectedRow].row);
}
This is proving to be problematic. textFieldDidChange always returns [self.tableView indexPathForSelectedRow].row as 0, as the cell of course, has never been selected. I'm stumped on how I could even find out which row's UITextField has been edited, so I can update the corresponding itemValues array.
UITableView has a neat method that converts a point in the tableView to an indexPath, indexPathForRowAtPoint:.
First you have to convert the origin of your textField to the frame of the UITableView.
- (void)textFieldDidChange:(UITextField *)sender
{
CGPoint textFieldOriginInTableView = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:textFieldOriginInTableView];
if (indexPath) {
NSLog(#"TextField at indexPath %# did change", indexPath);
}
else {
NSLog(#"Error: Can't calculate indexPath");
}
}
The easiest way to do this is to just tag the textfield with indexPath.row and get it back via [sender tag] in the delegate method.

UISwitch in UITableView Bug

I have an UISwitch in my UITableViewCell prototype.
The problem is, when I turn on one of the switches, one of the not shown switches also gets turned on.
This doesn't happen in the iPad version where all cells are shown.
Here is some code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
Notification *notification = [notifications objectAtIndex:indexPath.row];
UILabel *titleLabel = (UILabel *) [cell viewWithTag:100];
UISwitch *newsSwitch = (UISwitch *) [cell viewWithTag:101];
UIImageView *imageView = (UIImageView *) [cell viewWithTag:102];
[titleLabel setText:[notification name]];
[imageView setImage:[UIImage imageNamed:[notification image]]];
BOOL isOn = [storage boolForKey:[NSString stringWithFormat:#"notification_%#", [notification name]]];
[newsSwitch setOn:isOn];
[newsSwitch setTag:indexPath.row];
[newsSwitch addTarget:self action:#selector(didChangeStateForSwitch:) forControlEvents:UIControlEventValueChanged];
return cell;
}
Your problem is that you first query the switch via its tag:
UISwitch *newsSwitch = (UISwitch *) [cell viewWithTag:101];
But later on, you change that tag:
[newsSwitch setTag:indexPath.row];
So when the cell gets reused, the switch won't be found as now its tag isn't 101 any more. Therefor, the switch will be stuck in its old state.
You can easily verify this by adding a NSLog(#"Switch: %#", newsSwitch); after querying the switch. You'll see that it'll output Switch: (null) for those rows where you have "wrong" switch values.
The solution this is to not modify the tag.
Question is, how do you remember which row the switch is for, then? One way would be this:
- (void)didChangeStateForSwitch:(id)sender
{
NSIndexPath *indexPath = [myTableView indexPathForCell:[sender superview]];
...
}
Another possibility is to use associated objects.
First time when you load the cell you are taking the view with tag 101 for switch.Few lines after that you are setting new tag to this switch and next time when you try to take the view with tag 101 it doesn't exist.[newsSwitch setTag:indexPath.row]; delete this line and try again
You can get the index path as #DarkDust suggested
- (void)didChangeStateForSwitch:(id)sender
{
NSIndexPath *indexPath = [myTableView indexPathForCell:[sender superview]];
...
}

Get IndexPath.Row from TableView Objective C [duplicate]

This question already has answers here:
indexPathForCell returns nil since ios7
(3 answers)
Closed 9 years ago.
Before asking this question, I searched a lot on Google and Stackoverflow. Tried also some examples, but I can't make the function work.
Since the hierarchy of the tableview is changed since iOS 7, is it kind of hard to find a solution.
I got a standard tableview with a couple items and one button on the screen.
I need to get the indexPath.row number when selecting an item from the tableview and clicking on the button.
This is my code
- (IBAction)buttonGetNumber:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)[(UIView *)[button superview] superview]];
NSLog(#"%i", indexPath.row);
}
This keeps returning a '0', no matter which item I select from the tableview.
I also tried this (2):
- (IBAction)buttonGetNumber:(id)sender {
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *) [[button superview] superview];
NSIndexPath *index = [self.tableView indexPathForCell:cell];
NSLog(#"%i", index.row);
}
This also returns a '0'.
I also tried this (3):
- (IBAction)buttonGetNumber:(id)sender {
UIButton *senderButton = (UIButton *)sender;
UITableViewCell *buttonCell = (UITableViewCell *)[[senderButton superview] superview];
UITableView* table = (UITableView *)[buttonCell superview];
NSIndexPath* pathOfTheCell = [table indexPathForCell:buttonCell];
NSInteger rowOfTheCell = [pathOfTheCell row];
NSLog(#"%i", rowOfTheCell);
}
And this makes the application crash.
Any clue how I can solve this?
Create an instance variable _lastClickedRow Set it with tableview delegate like below. And when you click the to "Get Row" button use _lastClickedRow.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
_lastClickedRow = indexPath.row;
}
- (IBAction)buttonGetNumber:(id)sender {
NSLog(#"%d" , _lastClickedRow);
}
If for some reason selected cell doesn't work for you (e.g. for a multiple selection case), you could get row from sender's frame:
- (IBAction)buttonGetNumber:(id)sender
{
CGPoint buttonOrigin = sender.frame.origin;
CGPoint pointInTableview = [self.tableView convertPoint:buttonOrigin fromView:sender.superview];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:pointInTableview];
if (indexPath) {
// Do your work
}
}
You simply set the tag of the UIButton same as the indexPath.row using:
yourButton.tag = indexPath.row;
in didSelectRowForIndexPath:.
Then in buttonGetNumber: method, you get the row number using:
int rowNum = [(UIButton*)sender tag];
Here, you have the advantage of not using any third variable.

UITableView indexPath.row issue

I am using a tableView which loads a custom UITableViewCell with a "Tap" button inside it. A method is called when user clicks the button.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{...
[btnRowTap addTarget:self action:#selector(didButtonTouchUpInside:) forControlEvents:UIControlEventTouchDown];
...
return cell;
}
In the didButtonTouchUpInside method, Im trying to retrieve the value of row selected in the following way:
-(IBAction)didButtonTouchUpInside:(id)sender{
UIButton *btn = (UIButton *) sender;
UITableViewCell *cell = (UITableViewCell *)btn.superview;
NSIndexPath *indexPath = [matchingCustTable indexPathForCell:cell];
NSLog(#"%d",indexPath.row);
}
The problem is that on clicking button at any row, I am getting the same value of 0 every time.
Where am I going wrong?
You must NOT rely on the view hierarchy of a UITableViewCell. This approach will fail in iOS7, because iOS7 changes the view hierarchy of the cell. There will be an additional view between your button and the UITableViewCell.
There are better ways to handle this.
Convert the button frame so it is relative to the tableview
ask the tableView for the indexPath at the origin of the new frame
.
-(IBAction)didButtonTouchUpInside:(id)sender{
UIButton *btn = (UIButton *) sender;
CGRect buttonFrameInTableView = [btn convertRect:btn.bounds toView:matchingCustTable];
NSIndexPath *indexPath = [matchingCustTable indexPathForRowAtPoint:buttonFrameInTableView.origin];
NSLog(#"%d",indexPath.row);
}
set Button tag in to cellForRowAtIndexPath method Befor setting Method like
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{...
btnRowTap.tag=indexPath.row
[btnRowTap addTarget:self action:#selector(didButtonTouchUpInside:) forControlEvents:UIControlEventTouchDown];
...
return cell;
}
and your tapped cell getting like this:-
-(IBAction)didButtonTouchUpInside:(id)sender{
{
UIButton *button = (UIButton*)sender;
NSIndexPath *indPath = [NSIndexPath indexPathForRow:button.tag inSection:0];
//Type cast it to CustomCell
UITableViewCell *cell = (UITableViewCell*)[tblView1 cellForRowAtIndexPath:indPath];
NSLog(#"%d",indPath.row);
}
try like this,if you are adding button to cell content view then use below code.
UITableViewCell *buttonCell = (UITableViewCell *)sender.superview.superview;
UITableView* table1 = (UITableView *)[buttonCell superview];
NSIndexPath* pathOfTheCell = [table1 indexPathForCell:buttonCell];
int rowOfTheCell = [pathOfTheCell row];
int sectionOfTheCell = [pathOfTheCell section];
btn.superview is contentView of UITableviewCell. Use btn.superview.superview instead.
if you already knew the value inside the cell, then
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *currentCell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([currentCell.textLabel.text isEqualToString:#"Your Cell Text value" ]){
//Do theStuff here
}
}
here is the code for your ibAction.you dont need to set any tag or anything else
-(IBAction)didButtonTouchUpInside:(id)sender{
NSIndexPath *indexPath =
[tbl
indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
}

Resources