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];
}
Related
I've UITableView and its cell have one UITextField.
Now I added long gesture in UITextField but it not working. When I tap long gesture on textfield it always show context menu (select,copy cut,past,etc.).
My question is that how to manage long gesture as well as context menu in UITextFiled.
I've tried below code:
longGesture = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
longGesture.minimumPressDuration = 2.0; //seconds
longGesture.delegate = self;
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil) {
NSLog(#"long press on table view but not on a row");
} else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
NSLog(#"long press on table view at row %ld", indexPath.row);
} else {
NSLog(#"gestureRecognizer.state = %ld", gestureRecognizer.state);
}
}
Tableview delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Note *obj = [self.dataArr objectAtIndex:indexPath.row];
TableViewCell *Cell = [self.tableView dequeueReusableCellWithIdentifier:#"cell"];
if (Cell == nil) {
Cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
Cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else{
Cell.memoField.text = obj.memoRowText;
}
Cell.memoField.userInteractionEnabled = YES;
[Cell.memoField addGestureRecognizer:longGesture];
Cell.memoField.delegate = self;
Cell.memoField.tag = indexPath.row;
return Cell;
}
You'll want to set up a failure requirement between the gesture that shows the context menu and your long press gesture. Specifically, you'll want the menu recognizer to require your long press to fail (i.e. you'll want the menu recognizer to wait until it has ruled out the long press). In code, one way to do that is this is implementing this delegate method.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)longPress shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)other {
if (other.view /* is a text field in the table view */) {
return YES;
} else {
return NO;
}
}
These methods can be a little confusing.
Remember that you can add "static" failure requirements with -[UIGestureRecognizer requireGestureRecognizerToFail:], but in many cases you don't necessary easily have references to both recognizers (such as in this case).
In many cases, this suffices.
However, the gesture recognizer system also gives you a chance to install failure requirements "on the fly".
Returning YES from -gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer: has the same effect as if you called [second requireFailureOfGestureRecognizer:first] (where first and second are the first and second arguments to that method).
OTOH returning YES from -gestureRecognizer:shouldRequireFailureOfGestureRecognizer: has the same effect as if you called [first requireFailureOfGestureRecognizer:second].
This is the type of screen I'm making:
So when the UISwitch state changes it should change the label to ON or OFF. In my cellForRowAtIndexPath I'm calling [cell.mainSwitch addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; and switchChanged is as follow:
-(void) switchChanged:(id)sender {
UISwitch* cellNew = (UISwitch*)sender;
if ([cellNew isOn]) {
cell.funcStatus.text = [funcStatusArr objectAtIndex:0];
} else {
cell.funcStatus.text = [funcStatusArr objectAtIndex:1];
}
}
Now the problem I'm facing is that it is not changing the Label at the specific cell butt all the switches are changing the Label of 4th(last) cell as shown in the figure below.
As you can see that the first label is off but it is changing the label of last row. Any ideas why it is happening or how to tell the functions that this index.row is sending the request.
So u must add tag property to all UISwitch. Fasters way its in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath call
// code
// its good work if u have only 1 section
mySwitch.tag = indexPath.row.
//code
Than fix u're code
-(void) switchChanged:(UISwitch*)switch {
SettingsTableViewCell *selectedCell;
NSIndexPath *selectedIndexPath;
if(switch.tag == 0)
// create selectedIndexPath with correctrly row and section
selectedIndexPath = [NSIndexPath indexPathForRow:'YourRow'inSection:'Your section']
// create cell
selectedCell = [self.tableView cellForIndexPath:selectedIndexPath];
} else if(switch.tag == 1) {
// same logic;
}
// and replace text for selected cell
if ([switch isOn]) {
selectedCell.funcStatus.text = [funcStatusArr objectAtIndex:0];
} else {
selectedCell.funcStatus.text = [funcStatusArr objectAtIndex:1];
}
}
it's must work.
You doing wrong thing.First you need to get cell at specific changed event.
follow this code.
-(void) switchChanged:(id)sender {
UISwitch* switchBtn = (UISwitch*)sender;
//self.tableView is UITableView's outlet named tableView
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
//First detect Tableview's Cell then do the stuff
//CustomCellTableViewCell replace it with you custom cell class
CustomCellTableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if ([switchBtn isOn]) {
cell.funcStatus.text = [funcStatusArr objectAtIndex:0];
} else {
cell.funcStatus.text = [funcStatusArr objectAtIndex:1];
}
[self.tableView reloadRowsAtIndexPaths:[[NSArray alloc] initWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
}
Cheers.
I have a UIImageView inside UITaleViewCell, I added a tap recognizer to the UIImageView.
- (IBAction)ui_tapped:(UITapGestureRecognizer *)sender {
NSIndexPath *indexPath = [CocoaHelper indexPathWithTableView:self.tableView sender:sender.view];
RichMediaViewController *viewController = (RichMediaViewController *)[CocoaHelper viewControllerWithIdentifier:VC_RICH_MEDIA];
Message *message = self.messages[indexPath.row];
[viewController setupWithEntity:message];
[self presentViewController:viewController animated:YES completion:nil];
}
+ (NSIndexPath *)indexPathWithTableView:(UITableView *)tableView sender:(id)sender {
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:tableView];
NSIndexPath *hitIndex = [tableView indexPathForRowAtPoint:hitPoint];
return hitIndex;
}
I used + (NSIndexPath *)indexPathWithTableView:(UITableView *)tableView sender:(id)sender
for buttons inside cells and it is correct, but for gesture, it always returns the cell of the last index path.
EDIT
Hard-coding the tap recognizer works
if ([message.type isEqualToString:#"image"]) {
UIImageView *view = ((ImageMessageCell *)cell).imageView;
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(ui_tapped:)];
[view setGestureRecognizers:[NSArray arrayWithObject:tapRec]];
}
This is very tedious coding (since I need to check for each types of cells including image, video and other rich media), please post your answer if you know how to add it from the storyboard
It turns out that it's not able to add gesture recognizer to each instantiated prototype cell separately, i.e. only one gesture recognizer for all the cells instantiated from the same prototype cell.
Solved this problem by using UIButton with background image view instead of using image view.
[self.imageButton setBackgroundImage:[UIImage imageNamed:LOADING_IMAGE_FILE] forState:UIControlStateNormal];
NSData *blob = post.thumbnailBlob;
if (blob) {
[self.imageButton setBackgroundImage:[UIImage imageWithData:blob] forState:UIControlStateNormal];
}
In order for each cell to have it's unique tap gesture tag, it needs to be done in a proper sequence:
let tapGest = UITapGestureRecognizer(target: self, action: #selector(yourTapFunction(_:)))
//1 add gesture to the cell
cell.imageView.addGestureRecognizer(tapGest)
//2 assign a tag
tapGest.view?.tag = indexPath.row
#objc func yourTapFunction(_ tapGest: UITapGestureRecognizer) {
guard let index = tapGest.view?.tag else { return }
//do smth
}
if you reverse 1 and 2, the tag will always be 0
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.
I have UITableView that contains many cell. User can expand cell to see more content in this cell by push the expand button in this cell (only 1 cell can expand at time):
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedRowIndex == indexPath.row) return 205;
else return 60;
}
In the storyboard, I drag UILongPressGesture into cell button and named it longPress (cell is custom, it has 2 buttons in it, 1 need to recognize LongPressGesture, the other expand cell height):
#property (retain, nonatomic) IBOutlet UILongPressGestureRecognizer *longPress;
And in the viewDidLoad:
- (void)viewDidLoad
{
[longPress addTarget:self action:#selector(handleLongPress:)];
}
It's work perfectly, however when I use following code to recognize cell indexPath, it's wrong when one cell is expanded:
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
// Get index path
slidePickerPoint = [sender locationInView:self.tableView];
NSIndexPath *indexPath= [self.tableView indexPathForRowAtPoint:slidePickerPoint];
// It's wrong when 1 cell is expand and the cell's button I hold is below the expand button
}
Can anyone please show me how to get correct indexPath when there're different cell height?
Thank in advance
One way to do it would be to add a UILongPressGestureRecognizer to each UITableViewCell (that all use the same selector), then when the selector is called you can get the cell via sender.view. Perhaps not the most memory efficient, but if the single gesture recognizer won't return the right row in certain situations, this way should work.
Something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
[longPress setMinimumPressDuration:2.0];
[cell addGestureRecognizer:longPress];
[longPress release];
return cell;
}
then
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
UITableViewCell *selectedCell = sender.view;
}
First add the long press gesture recognizer to the table view:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.myTableView addGestureRecognizer:lpgr];
[lpgr release];
Then in the gesture handler:
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
CGPoint p = [gestureRecognizer locationInView:self.myTableView];
NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:p];
if (indexPath == nil)
NSLog(#"long press on table view but not on a row");
else
NSLog(#"long press on table view at row %d", indexPath.row);
}
}
You have to be careful with this so that it doesn't interfere with the user's normal tapping of the cell and also note that handleLongPress may fire multiple times before user lifts their finger.
Thanks...!