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.
Related
I understand this is suppose to happen but I haven't been able to find a way to call this method when the button is tapped The method gets called but the wrong cell is selected.
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object{
_postCell = (postCell *) [self.tableView dequeueReusableCellWithIdentifier:#"postCell"];
_postCell.personStringPost.text = [object objectForKey:#"stringPost"];
[_postCell.nameLabel setTitle:[object objectForKey:#"User_Name"] forState:UIControlStateNormal];
_postCell.userId = [object objectForKey:#"userId"];
_postCell.selectionStyle = UITableViewCellSelectionStyleNone;
PFFile *imageFile = [object objectForKey:#"profileImage"];
NSData *data = [imageFile getData];
_postCell.profileImage.image = [UIImage imageWithData:data];
[_postCell.nameLabel addTarget:self action:#selector(personProfile:tableView:) forControlEvents: UIControlEventTouchUpInside];
[_postCell.profileImageButton addTarget:self action:#selector(personProfile:tableView:) forControlEvents:UIControlEventTouchUpInside];
return _postCell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
postCell *cell = (postCell *)[tableView cellForRowAtIndexPath:indexPath];
self.userId = cell.userId;
NSLog(#"Did Select: %#", self.userId);
}
- (void) personProfile: (NSIndexPath *) indexPath tableView: (UITableView *) tableView{
[self tableView:tableView didSelectRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"personProfile" sender:self];
}
Few things i hope it will help
while dealing with button with only one section
in cellfor row method
yourcell.mybutton.tag=indexPath.row;
-(void)myCellbuttonAction:(id)sender
{
UIButton *button = (UIButton *)sender; // first, cast the sender to UIButton
NSInteger row = button.tag; // recover the row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
// apply your logic for indexpath as in didselect row
}
While dealing with multiple sections and multiple rows it this might help you
in cell for row method
yourcell.tag=indexPath.section;
yourcell.contentview.tag=indexPath.row;
and your button action might look like this
-(void)myCellbuttonAction:(id)sender
{
UIButton *button = (UIButton *)sender; // first, cast the sender to UIButton
id rowid =[button superview];
id sectionid = [rwoid superview];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[rowid tag] inSection:[sectionid tag]];
// apply your logic for indexpath as in didselect row
}
A few things here.
a) I would not use an instance variable (_postCell) in your cellForRowAtIndex method. You will likely have cell re-use problems and this may well be the source of your error. Replace it with a local variable:
postcell *cell = (postCell *)[self.tableView dequeueReusableCellWithIdentifier:#"postCell"];
You will also need to replace all references to _postCell with cell.
b) Note that in the same line your cast uses lowercase = (postCell *)... - I have done the same above, but it is best practice for class names to start with capital letters.
c) You have a property named nameLabel, which suggests its a UILabel, but you are using the setTitle:forState: method, which implies it's a UIButton. I would rename this property since debugging will be a lot easier if the names match the classes (or at least don't imply the wrong class).
d) When you call the addTarget:action:forControlEvents method, your selector is for personProfile:tableView:. The signature for that method is for an NSIndexPath and a UITableView. But your button will not be sending those arguments of those types. It will send details of the sender - i.e. the button which triggered the action. So you need to revise your method to accept arguments of that type:
[cell.nameLabel addTarget:self action:#selector(buttonPressed:) forControlEvents: UIControlEventTouchUpInside];
e) When the method gets called, you need a way to determine which cell the sending button was on. Ideally you would subclass UIButton to add some link to the cell, but (if you have only one section) you might get away with putting the row number as a tag. To do this add:
cell.nameLabel.tag = indexPath.row;
to your cellForRowAtIndexPath:. Then you can implement a different method to handle the button press, as follows:
-(void)buttonPressed:(id)sender {
UIButton *button = (UIButton *)sender; // first, cast the sender to UIButton
NSInteger row = button.tag; // recover the row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0]; // derive the indexPath, assuming section is 0
[self.tableView selectRowAtIndexPath:indexPath animated:YES]; // select the relevant row in the table (assuming the table is self.tableView)
[self performSegueWithIdentifier:#"personProfile" sender:self]; // perform the segue
}
f) Note in the above that you should not call tableView:didSelectRowAtIndexPath:. This is for the tableView to call when the user selects the row, not for selecting rows programmatically. Use selectRowAtIndexPath:animated: instead.
This is basically pbasdf answer above with Swift 3
I do call didSelectRowAt directly since it is not triggered by selectRowAtIndexPath
The tag is set on cell creation with the row of the indexpath
The getTableView function is my own
#IBAction func actionButtonPushed(_ sender: Any) {
guard let button = sender as? UIButton else { return }
guard let tableView = getTableView() else { return }
let indexPath = IndexPath(row: button.tag, section: 0)
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
if let delegate = tableView.delegate {
delegate.tableView!(tableView, didSelectRowAt: indexPath)
}
}
This question already has answers here:
Getting row of UITableView cell on button press
(10 answers)
Closed 9 years ago.
I have a custom method to detect a tap on a cell's image. I want to also find the index path of the image's correlating cell, and use it within the function. Here is what I am using:
CellforRowAtIndexPath:
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cellImageTapped:)];
tapped.numberOfTapsRequired = 1;
[cell.imageView addGestureRecognizer:tapped];
method im trying to get the index path at:
-(void)cellImageTapped:(id)sender {
if(videoArray.count > 0){
Video *currentVideo = [videoArray objectAtIndex:INDEX_PATH_OF_CELL_IMAGE];
//do some stuff
}
}
I have no idea how to pass the index path. Any ideas?
Simple way:
Get the point of touch
Then get index path of cell at point
The code is:
-(void)cellImageTapped:(id)sender {
UITapGestureRecognizer *tap = (UITapGestureRecognizer *)sender;
CGPoint point = [tap locationInView:theTableView];
NSIndexPath *theIndexPath = [theTableView indexPathForRowAtPoint:point];
if(videoArray.count > 0){
Video *currentVideo = [videoArray objectAtIndex:theIndexPath];
//do some stuff
}
}
I would recommend this way to fetch indexPath of cell which has custom subview - (compatible with iOS 7 as well as all previous versions)
- (void)cellImageTapped:(UIGestureRecognizer *)gestureRecognizer
{
UIView *parentCell = gestureRecognizer.view.superview;
while (![parentCell isKindOfClass:[UITableViewCell class]]) { // iOS 7 onwards the table cell hierachy has changed.
parentCell = parentCell.superview;
}
UIView *parentView = parentCell.superview;
while (![parentView isKindOfClass:[UITableView class]]) { // iOS 7 onwards the table cell hierachy has changed.
parentView = parentView.superview;
}
UITableView *tableView = (UITableView *)parentView;
NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)parentCell];
NSLog(#"indexPath = %#", indexPath);
}
Add a tag to the UIImageView in your UITableViewDataSource's tableView:cellForRowAtIndexPath: method.
cell.imageView.tag = indexPath.row;
I ended up using the sender's view's tag. Hopefully this will help someone, as I wasted an hour finding the answer.
-(void)cellImageTapped:(id)sender {
UITapGestureRecognizer *gesture = (UITapGestureRecognizer *) sender;
if(videoArray.count > 0){
NSInteger datIndex = gesture.view.tag;
Video *currentVideo = [videoArray objectAtIndex:datIndex];
}
}
Use the delegate method didSelectRowAtIndexPath: method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self cellImageTapped:indexPath];
}
Then you can pass the index to a function i.e.
-(void)cellImageTapped:(NSIndexPath *)indexPath
{
Video *currentVideo = [videoArray objectAtIndex:indexPath.row];
}
my app was running fine under ios6.1. tried the ios7 simulator and the following part does not work:
EditingCell *cell = (EditingCell*) [[textField superview] superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSLog(#"the section is %d and row is %d", indexPath.section, indexPath.row);
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *rowKey = [[keysForRows objectAtIndex: section] objectAtIndex: row];
It always comes:
the section is 0 and row is 0
although another section / row were selected.
Has someone an idea why this does not work under ios7?
Your approach to find the "enclosing" table view cell of a text field is fragile,
because is assumes a fixed view hierarchy (which seems to have changed between
iOS 6 and iOS 7).
One possible solution would be to traverse up in the view hierarchy until the table view cell is found:
UIView *view = textField;
while (view != nil && ![view isKindOfClass:[UITableViewCell class]]) {
view = [view superview];
}
EditingCell *cell = (EditingCell *)view;
A completely different, but often used method is to "tag" the text field with the row
number:
cell.textField.tag = indexPath.row; // in cellForRowAtIndexPath
and then just use that tag in the text field delegate methods.
I was finding cells the same way you were. Now I use this quick method if I have a button in a cell and know the tableview I'm in. It'll return the tableviewcell.
-(UITableViewCell*)GetCellFromTableView:(UITableView*)tableView Sender:(id)sender {
CGPoint pos = [sender convertPoint:CGPointZero toView:tableView];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:pos];
return [tableView cellForRowAtIndexPath:indexPath];
}
Experiencing this problem in iOS 11, but not in 9 or 10, I overrode the func indexPath(for cell: UITableViewCell) -> IndexPath? method using the technique that #drexel-sharp detailed previously:
override func indexPath(for cell: UITableViewCell) -> IndexPath? {
var indexPath = super.indexPath(for: cell)
if indexPath == nil { // TODO: iOS 11 Bug?
let point = cell.convert(CGPoint.zero, to: self)
indexPath = indexPathForRow(at: point)
}
return indexPath
}
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]];
}
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!