UITableView indexPath.row issue - ios

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

Related

How to manage custom cell label value in iOS?

I’m building an iOS app using storyboards.I have used UITableViewController which has 6 custom cells each of which contains three IBOutlet buttons and one IBOutlet label.
When the user clicks on any button in one particular custom cell, then the value of only that particular cell label should change.
But what happens is, values of all labels in each custom cell get changed.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
cell1.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
long row = [indexPath row];
cell1.First.tag=row;//button iboutlet in cell
cell1.Second.tag=row;//button iboutlet in cell
cell1.Third.tag=row;//button iboutlet in cell
cell1.Level.tag=row;
cell1.Level.text=Skill;//label iboutlet in cell
return cell1;
}
-(IBAction)FirstAction:(id)sender{
Skill=#"first";
[self.tableView reloadData];
}
-(IBAction)SecondAction:(id)sender{
Skill=#"Second";
[self.tableView reloadData];
}
-(IBAction)ThirdAction:(id)sender{
Skill=#"Third";
[self.tableView reloadData];
}
A couple of issues:
You should have a model that reflects what's in your table view. Specifically, right now you have a single Skill value, but you have six rows. You want to maintain a model that is an array of values, something like:
#property (nonatomic, strong) NSMutableArray *values;
And
- (void)viewDidLoad {
[super viewDidLoad];
self.values = [#[#"first", #"first", #"first", #"first", #"first", #"first"] mutableCopy];
}
And
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
NSString *value = self.values[indexPath.row];
cell.level.text = value;
return cell;
}
Note, no tag numbers needed.
When you tap on a button you must (a) identify what row in the table that corresponds to; (b) update that row in your model; and (c) reload that single row in the table (which will look up the value in the model):
- (void)updateCell:(UIView *)cell withValue:(NSString *)value {
NSParameterAssert(cell);
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)cell];
if (cell) {
self.values[indexPath.row] = value;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (IBAction)didTapFirstButton:(id)sender {
[self updateCell:[[sender superview] superview] withValue:#"first"];
}
- (IBAction)didTapSecondButton:(id)sender{
[self updateCell:[[sender superview] superview] withValue:#"second"];
}
- (IBAction)didTapThirdButton:(id)sender{
[self updateCell:[[sender superview] superview] withValue:#"third"];
}
Note, the sender is the button. So I get the table view cell by grabbing the superview (which is the cell's content view) and then grab its superview (the cell itself). If your view hierarchy is different, then change that as appropriate, but hopefully this illustrates the idea.
First you have to do is that use delegate method selectRowAtIndexPath when the row is selected then the indexPath selected row buttons can be used...
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
}
when the row is selected then that desired rows button functions will be performed
Try below code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
cell1.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
long row = [indexPath row];
cell1.First.tag=row;//button iboutlet in cell
cell1.Second.tag=row;//button iboutlet in cell
cell1.Third.tag=row;//button iboutlet in cell
cell1.Level.tag=row;
cell1.Level.text=Skill;//label iboutlet in cell
[cell1.First addTarget:self action:#selector(FirstAction:) forControlEvents:UIControlEventTouchUpInside];
[cell1.Second addTarget:self action:#selector(SecondAction:) forControlEvents:UIControlEventTouchUpInside];
[cell1.Third addTarget:self action:#selector(ThirdAction:) forControlEvents:UIControlEventTouchUpInside];
return cell1;
}
In your IBOutlet methods add this to find the indexPath for the row
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
instead of reloading the entire tableview you only want to reload the text for that cell at that specified indexPath
To reload only one cell, do this instead of [self.tableView reloadData];
[self.tableView reloadRowsAtIndexPaths:#[hitIndex] withRowAnimation:UITableViewRowAnimationNone];
There's a few ways to do it. But, from looking at your code I would make sure you get the right indexPath from the cell you selected. Assuming you have the target action already set up, you could rewrite your IBAction method like so:
-(IBAction)firstAction:(UIButton *)sender{
int row = sender.tag;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
Cell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
Skill = #"first";
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
this is going to get the right cell and index path. Update that cell's label only and then reload it.

Select tableViewCell on button click

I have a tableView, with custom cells.
In each cell i have a UIButton. When i click the button inside the cell i want that cell to highlight, but when i click o the cell i don't want this to happen.
Do you know any way to do this?
thank you
until now i have this code:
- (IBAction)buttonMethod: (id)sender {
UIButton *b = (UIButton *) sender;
UITableViewCell *cell = (UITableViewCell *)[[[sender superview] superview] superview];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
[cell setSelected:YES animated:YES];
}
but the cell is highlighted also in -didSelectRow.
- (UITableViewCell *)tableView: (UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
NSAssert(cell, #"cell -> nil");
NSDictionary *cellData = [_data objectAtIndex:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
[cell populateCellWithData:cellData];
return cell;
}
Use this UITableViewDelegate method:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
This way, cells can't be selected when the user taps on them, but you can still select them programatically.
Also using
selectRowAtIndexPath:animated:scrollPosition:
on your UITableView , instead of setSelected:animated: on UITableViewCell might be better. Cells can be reused and the selection will probably disappear when it happens.
You have to add following line in tableView delegate method cellForRowATIndexPath.
cell.selectionStyle =- UITableViewCellSelectionStyleNone;
You have to set cell.selectionStyle = UITableViewCellSelectionStyleNone; in cellForRowAtIndexPath method of tableView.
So you can set this style in - (IBAction)buttonMethod: (id)sender. You can add this line at the end. See below:
- (IBAction)buttonMethod: (id)sender {
UIButton *b = (UIButton *) sender;
UITableViewCell *cell = (UITableViewCell *)[[[sender superview] superview] superview];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
[cell setSelected:YES animated:YES];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
Just use UITableViewDelegate like this
-(BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath{
return NO;
}
but If I were you, I would change the background color of cell in [buttonMethod:] with UITableViewCellSelectionStyleNone

Button inside UITableCell - passing data to selector

This subject is all over the stackoverflow but I couldn't find a fitting solution.
I have button on each UITableViewCell.
Here is how I create the cell.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ExaminationCell"];
UIButton *clipButton = (UIButton *)[cell viewWithTag:3];
MedicalExamination *examination = [objects objectAtIndex:indexPath.row];
[clipButton addTarget:self action:#selector(examinatonClicked:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
Here is the method thah should handle the button click:
-(void)examinatonClicked:(MedicalExamination*)examination
{
}
How do I pass examination object to examinatonCommentsClicked method? Obviously the object is different for each cell...
A somewhat hacky thing that I'll do, is I'll use the UIButton's tag attribute to pass the row number so I can pull the object from the array that's backing my table.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ExaminationCell"];
UIButton *clipButton = (UIButton *)[cell viewWithTag:3];
clipButton.tag = indexPath.row //Set the UIButton's tag to the row.
MedicalExamination *examination = [objects objectAtIndex:indexPath.row];
[clipButton addTarget:self action:#selector(examinatonClicked:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
-(void)examinatonClicked: (id)sender
{
int row = ((UIButton *)sender).tag;
MedicalExamination *examination = [objects objectAtIndex: row];
//Profit!
}
I think you have to add button in uitableviewCell through xib(custum cell) and then connect it through iboutlet.

scrollToRowAtIndexPath when Button inside UITableViewCell is pressed

I have a tableview with a few custom cells. Inside the first one is a button. When this button is pressed I want to scroll to a certain Section in my tableview. How do I have to link the button action with the tableview?
You can scroll to a certain section with using this function :
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
the example of usage is :
[tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:5 inSection:indexPath.section]
atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
and for link the button action with tableview you can use protocol in you custom cell
You can make your cell's button a property and in cellForRowAtIndexPath you can set the target in the class that loads the table view so you won't need any delegates. Something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"YourCellIdentifier";
YourCustomCell *cell =[tableView dequeueReusableCellWithIdentifier:identifier];
if(cell == nil) {
cell = [YourCustomCell alloc/init method....
[cell.buttonProperty addTarget:self action:#selector(cellButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
}
//do other stuff with your cell
}
-(void)cellButtonTapped:(id)sender {
UIButton *button = (UIButton*)sender;
YourCustomCell *cell = (YourCustomCell*)button.superview.superview; //if the button is added to cell contentView or button.superview is added directly to the cell
NSIndexPath *path = [yourTableView indexPathForCell:cell];
[yourTableView scrollToRowAtIndexPath:path
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
}
Set up a delegation mechanism back from your cell - when creating the cell, assign it an NSIndexPath and pass it back from the cell when the button is tapped.
So in your UITableViewCell subclass you'll have:
- (IBAction)buttonPressed:(id)sender
{
[self.delegate buttonPressedOnCellAtIndexPath:cell.indexPath]
}
Back in the controller that's the delegate, respond to this method with:
- (void)buttonPressedOnCellAtIndexPath:(NSIndexPath)indexPath
{
[self.tableView scrollToRowAtIndexPath:indexPath];
}

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

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!

Resources