iphone - perform segue from custom TableCellView - ios

I have a UITableView in which I am populating custom built UITableViewCells. These have a picture and a few labels.
Is there any way that when click on the picture that it performs a certain segue but when I click on each of the labels it performs different segues. I only want these segues performed when I click on the UIImageView or the UILabels.
I am currently playing with the following idea.
Add gesture recogniser to UIImageView and UILables when creating them in cellForRowAtIndexPath
The above touch gesture will trigger the segue
My code looks like this in the UITableView cellForRowAtIndexPath delegate where I create my UITableViewCells
cell.myImage.userInteractionEnabled = YES;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething:)];
singleTap.numberOfTapsRequired = 1;
singleTap.delegate = self;
[cell.myImage addGestureRecognizer:singleTap];
This overrides the didSelectRowAtIndexPath delegate for the table view when I click on the UIImageView so I've tried triggering the segue from the doSomething: function but this function doesn't know the UITableView indexPath so cannot send the right information to the destination viewcontroller (it always sends 0).
I'm sure there must be an easy away to do this
Any ideas welcome. Thanks in advance.

You can get NSIndexPath of cell myImage belongs to by adding following to tap handler doSomething:
- (void)doSomething:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
CGPoint touch = [sender locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:touchPoint];
NSLog(#"indexPath: %#", indexPath);
//Select cell and trigger didSelectRowAtIndexPath:
[self.myTableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
[self tableView:self.myTableView didSelectRowAtIndexPath:indexPath];
}
}

UIGestureRecognizers have a view property you can access. Iterate over that view's superviews until you have the UITableViewCell and call:
[tableView indexPathForCell:cell];
that should give you the IndexPath.

Related

iOS Disable Double Tap gesture recognizer in Swift

I am working on a app using TableView now i am facing an issue listed below.
Inside my TableView there is UITextView on it, that MUST be selectable, but not editable (because I need to use and proceed links).
My issue is:
when I tap on a link as everybody does, it doesn't work. I need to hold it a bit longer to make it work. I thought that it is because of "Selectable" property brings in a Double Tap Gesture recognizer, so my textView checks if there is a second tap, but I don't know how to find and remove only double tap recognizer.
What should I do?
Thank you.
Have you considered replacing the TextView with a UIWebView, and just do a loadHTMLString function?
This way when you tap on a link, it will open instantly? You can even have a UIWebView delegate and do what you want when the link is pressed(Custom UIWebView instead of auto opening in safari etc)
You've to handle tap event.. Through this code
tapGesture.numberOfTapsRequired = 1
OR
To do this, you will need to embed one in your UITableViewCell. But there's no need to create a custom cell. Here is the basic idea of what you will want to do:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UITextView *comment = [[UITextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, tableView.rowHeight)];
comment.editable = NO;
comment.delegate = self;
[cell.contentView addSubview:comment];
[comment release];
}
return cell;
}
You will, of course, need to set your rowHeight if you don't want the standard 44pt height that comes with the cell. And if you want actual cells, you'll need to add your own logic so that only the cell you want is a textView, but this is the basic idea. The rest is yours to customize to your fitting. Hope this helps
EDIT: to bypass the textView to get to your cell, there are two ways to go about this.
1) you can make a custom textView class and overwrite touchesBegan to send the message to super:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
}
this will send the touch events to its superview, which would be your tableView. Considering you didn't want to make custom UITableViewCells, I imagine you probably don't want to make a custom textView class either. Which leads me to option two.
2) when creating the textView, remove comment.editable = NO;. We need to keep it editable, but will fix that in a delegate method.
In your code, you will want to insert a textView delegate method and we'll do all our work from there:
EDIT: changing this code to use with a UITableViewController
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
// this method is called every time you touch in the textView, provided it's editable;
NSIndexPath *indexPath = [self.tableView indexPathForCell:textView.superview.superview];
// i know that looks a bit obscure, but calling superview the first time finds the contentView of your cell;
// calling it the second time returns the cell it's held in, which we can retrieve an index path from;
// this is the edited part;
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
// this programmatically selects the cell you've called behind the textView;
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
// this selects the cell under the textView;
return NO; // specifies you don't want to edit the textView;
}
If that's not what you wanted, just let me know and we'll get you sorted out
Finding and Removing Double Tap Gesture recognizer
Objective C
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
{
[(UITapGestureRecognizer *)gestureRecognizer setNumberOfTapsRequired:1];
gestureRecognizer.enabled = NO;
}
}
Swift
func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
{
if gestureRecognizer.isKindOfClass(UITapGestureRecognizer)
{
(gestureRecognizer as! UITapGestureRecognizer).numberOfTapsRequired = 1
gestureRecognizer.enabled = false
}
}

How to differenciate indexPath of first cell with indexPath of empty space at the end of table?

I use the following code to detect a click on a UITableView and take action depending on which cell is clicked, and which element in the cell was clicked, with a default action for any element that doesn't match.
-(void)addTapRecognizer {
// this is called when the view is created
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
singleTap.delegate = self;
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self.tableView addGestureRecognizer:singleTap];
}
- (void)handleTap:(UITapGestureRecognizer *)tap {
NSLog(#"tap detected!");
if (UIGestureRecognizerStateEnded != tap.state) {
return;
}
UITableView *tableView = (UITableView *)tap.view;
CGPoint p = [tap locationInView:tap.view];
NSIndexPath* indexPath = [tableView indexPathForRowAtPoint:p];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSLog(#"selectedIndex = %ld", (long)indexPath.row);
// take action depending where the cell was clicked
// with a default action if no element matches
MyTableViewCell *cell = (MyTableViewCell *) [self.tableView cellForRowAtIndexPath:indexPath];
CGPoint pointInCell = [tap locationInView:cell];
if(CGRectContainsPoint(cell.someImage.frame,pointInCell)) {
[self openItemID:[ItemList[indexPath.row] valueForKey:ID_ITEM]];
return;
}
if (...) {
...
return;
}
[self openItemID:[ItemList[indexPath.row] valueForKey:ID_ITEM]];
return;
}
My problem is that when there aren't enough cells to fill the screen (so for instance the table contains 2 cells and then blank space below), when the user clicks below the last cell, this is treated as a click on the first cell (the console logs "selectedIndex = 0" in both cases).
Is there a way to tell the difference between such a click, in the empty space at the end of the table, and a click on a "proper" cell of the table?
Is there a way to tell the difference between such a click, in the empty space at the end of the table, and a click on a "proper" cell of the table?
Yes. For the cheap and easy solution only do what you are trying to do if you actually get an indexPath:
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:p];
if(indexPath != nil){
// do everything in here
}
Basically, your indexPath is returning nil because it can't find a row. From the docs:
An index path representing the row and section associated with point, or nil if the point is out of the bounds of any row.
You could do it the way that you're currently doing it but is there any reason why you aren't using:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
This is a much more standard way of detecting the cell that the user tapped on.
This method won't be called if you tap on something that isn't a cell and has a number of other benefits. Firstly you get a reference to the tableView and the indexPath for free. But you also won't need any gesture recognisers this way either.
Try something like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// Do stuff with cell here...
}
Obviously this all assumes that you have correctly set a class as your table view's delegate.
NB: It's very easy to mistakenly write didDeselectRowAtIndexPath instead of didSelectRowAtIndexPath when using Xcode's autocompletion to do this. I always do this and then inevitably realise my mistake 20 minutes later.

Custom Cell button Click

My scenario is as follow's:
Custom Cell View
1) I am populating data from server in the custom cell view which is perfectly populated, i want a link store in my array which i want to open in browser, so i want to check which index.row button is clicked so that against that row i can get the url link and open it in browser, so far i have found the solution of button tags but that doesn't work's as well, as if we have two cell's in the screen at same time on click of button both button return's same tag.
2) As the image attached i have another storyboard in which i want to segue to a new view controller, same as mentioned above, i want to pass a specific post title and key as well.
I hope every thing is clear.
Thank's in advance.
To answer the first part of your question. Tags are a pain. It's better to pass along the UIButton as the sender, and, in the action for the button, then you can say something like:
-(void) buttonPresse:(UIButton *)sender
{
CGPoint location = [self.tableView convertPoint:sender.bounds.origin fromView:sender];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [self.table cellForRowAtIndexPath];
//Do what you want with the cell or indexPath
}
I have found the solution of button tags but that doesn't work's as well
Perform following steps to detect which button is selected.
Type the code snippet given below in cellForRowAtIndexPath: method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// DEFINE CELL
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// SET ACTION METHOD ON CALL BUTTON
UIButton *button = (UIButton *) [self.view viewWithTag:40];
[button setUserInteractionEnabled:YES];
[button addTarget:self action:#selector(checkButtonTapped:) forControlEvents:UIControlEventAllTouchEvents];
}
Add following method for checkButtonTapped:.
- (void)checkButtonTapped:(UIButton *)sender
{
// GET POINT OF THE BUTTON PRESSED
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
// GET INDEXPATH FOR THE CELL WHERE THE BUTTON IS PRESSED
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
//Use indxPath.row to get item
//Perform operations
}
}
There is another approach if you want to perform action according to the cell selected by the user.
For the case where you want to check which row of the cell is clicked you should use following method.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"ShowindexPath%ld", (long)indexPath.row); }
Here we check for the user interaction on cell. indexPath.row will return index number for the cell selected by user. You can perform other actions in place of NSLog method.
For the purpose to send value while performing segue use following steps:
Create property in the NewViewController.h file where you want to perform segue as follow:
#property (nonatomic) NSString* receivedValue;
#synthesize property by writing following code in NewViewController.m file within #implementation and #end
#synthesize receivedValue;
ImportNewViewController.h file in TableViewController.m file.
Go to prepareForSegue: method in the TableViewController.m file from where you want to send the value and write following code according to your class names within it.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NewViewController *nVC = [segue destinationViewController];
[nVC setReceivedValue:sendValue];
}
Here sendValue is the value that you want to send.
That's all.
Build and run your project and enjoy.
You can extend UITableViewCell and add a touple property to it. key containing the index.row and value containing the URL eg:- 1:"www.stackoverflow.com". When you click on a button, all you have to figure out is the index.row and from that you can get the value that you are looking for.
you can add target for button in cellForRowAtIndexPath like :
[cell.yourButton addTarget:self action:#selector(buttonAction:event:) forControlEvents:UIControlEventTouchUpInside];
here you can receive the action for this button
-(void)buttonAction:(UIButton *)sender event:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPos = [touch locationInView:self.tblview];
NSIndexPath *indexPath = [self.tblview indexPathForRowAtPoint:touchPos];
if(indexPath != nil){
// handle click event..
}
}
surely it will work.........

How do you get the indexPath of the cell you are tapping?

I am trying to execute an IBAction when a long-press is performed on a cell in a UITableView. The action involves the content of the cell so I need to get the indexPath in order to retrieve the content from a dictionary in local storage. The IBAction method is defined in the MasterViewController.m file which contains the UITableView methods and is subclassed from UITableViewController. I have tried all of the following, and they all return null instead of the indexPath for the cell.
UITableViewCell *cell = (UITableViewCell *)self;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
I have also seen a few years-old answers to similar questions that use the position of the cell in the view, but I can't get any of those to work either.
UPDATE:
The IBAction, sendToPB, is being defined in a subclass of UITableViewController. There is a long-press gesture recognizer added to the cell in Interface Builder, with Sent Actions connected to sendToPB. The action is supposed to be copying the content of the cell to the clipboard when you long-press on the cell in the table view. All the ways I have tried so far return null for indexPath.
- (IBAction)sendToPB:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSString *object = self.objects[indexPath.row];
UIPasteboard *pb = [UIPasteboard generalPasteboard];
NSString *pressedCellText = [[Data getAllNotes] objectForKey:object];
[pb setString: pressedCellText];
}
UPDATE:
I have found two problems with this approach. First, the long-press gesture doesn't actually select the row, which is why all of the options that used indexPathForSelectedRow don't work. Second, sender is the gesture recognizer, and not the cell or row, so using sender also produces a null value for indexPath. With these two factors in mind, how else can you detect which cell you performed the long-press on?
You can get indexPath Like This on longPressGesture!
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
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 if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
NSLog(#"long press on table view at row %d", indexPath.row);
}
else
{
NSLog(#"gestureRecognizer.state = %d", gestureRecognizer.state);
}
}
may Be this Link will help you a little more
Declare variable 1st in .h file as
NSIndexPath *hitIndex;
then on long press method u can get the position of cell & hence indexpath
(void)longPressMethod:(UIButton *)btn
{
CGPoint hitPoint = [btn convertPoint:CGPointZero toView:tbl_traits];
hitIndex = [tbl_traits indexPathForRowAtPoint:hitPoint];
}
You can do it using Gesture Recognizers. Hope these snippets help.......
In your .h file
#interface yourClass ()
{
UILongPressGestureRecognizer *longPressRecognizer;
}
In viewDidLoad,
longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressDetected:)];
longPressRecognizer.minimumPressDuration = 2;
longPressRecognizer.numberOfTouchesRequired = 1;
longPressRecognizer.delegate = self;
In cellForRowAtIndexPath, just before return statement
[cell addGestureRecognizer:longPressRecognizer];
And at the end,
- (void) longPressDetected:(UILongPressGestureRecognizer *)recognizer
{
UITableViewCell *selectedCell = (UITableViewCell *)recognizer.view;
// Your required code here
}
Edit: Thanks to #RegularExpression
First be sure to set the delegate of your tableView
self.myTableView.delegate = self
I believe this is what you are looking for:
Swift
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//save the indexPath.row as an integer inside a property or pass it to your action
}
You can then save the index of from the above method or simple call your action passing that index inside the above method.
Objective-C
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//save the indexPath.row as an integer inside a property or pass it to your action
}
If this is inside a UITableViewController subclass, then casting self (which is an instance of the UITableViewController subclass) to UITableViewCell will not return the cell selected.
There is really 2 ways of doing this:
1- The easy way: Don't use an IBAction and just implement the delegate methods:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
2- The relatively easy way: in your IBaction, you can get the selected cell by using the property:
self.tableview.indexPathForSelectedRow
This property will give you the indexpath of the selected cell.
If you attached the UILongPressGestureRecognizer to the cell by calling cell.addGestureRegognizer(_: _: _:) you should retrieve the cell from the sender by doing
let touchedView = sender.view as! UITableViewCell
Anyway, the best way to achieve this usually is by inspecting the dataSource instead of calling the UITableViewDataSource methods
You could subclass UITableViewCell and add an IVar for the index path and set that when cellForRowAtIndexPath is called for the UITableView dataSource protocol.

Custom UITableViewCell and IBAction

I have a custom UITableViewCell on which I have added a button, I have associated that button on an IBAction in my viewController. Now the problem that i am facing is how do I know from which cell that button was created. When I present my viewController which has a table in it and has multiple rows (custom UITableViewCell), now when the user presses the button the action is getting called, but how do I know which row was it.
Because based on the row index I need to store some value.
Edit: I have some clue on it now, but still I am not sure how will I do it, so it seems like on my tableViewController cellForRowAtIndexPath method I have to do something like this
[cell.button1 addTarget:self action:#selector(addToCart:) forControlEvents:UIControlEventTouchUpInside];
And then I have to write a method
-(IBAction) addToCart:(id) sender
But still what I don't know is how do i get the row index in my addToCart method.
Appreciate your help.
Ok, finally I got the answer, looking into different forums, people were suggesting to do something like this
in the custom table view controller in cellForRowAtIndexPath do this
cell.addToCart.tag = indexPath.row;
[cell.addToCart addTarget:self action:#selector(addToCart:)
forControlEvents:UIControlEventTouchUpInside];
where addToCart is name of UIButton in my customUITableViewCell. It didn't seems to work for me. So this is what I did
-(IBAction) addToCart:(id) sender{
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)
[[sender superview] superview]];
NSLog(#"The row id is %d", indexPath.row);
}
And then through interfacebuilder I associated the action of my button to addToCart IBAction on my table view controller.
Much less hackerific.
[cell.button1 addTarget:self action:#selector(addToCart:event:) forControlEvents:UIControlEventTouchUpInside];
- (void)addToCart:(id)sender event:(id)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
}
The accepted answer does not work any more. Please refer to this post
It does it that way:
- (void)checkButtonTapped:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
...
}
}
Many of the developer used custom view to show custom cell on Table view for older version of iOS. If you are one of them, then you will have to face a problem that your button click action will no longer work with iOS7.
How to resolve this:
You have two options:
Option 1: Create the new lay out with new table cell instead of taking view. and put all layouts again in table cell.
I know, this will require a lot of effort.If you don't want to do this, we have a very small hack for it:option 2
Option 2: Create a IBOutlet for your button and add this button as a subview of your cell's content view.
[self.myCell.contentView addSubview:self.btn_click];
The above line of code will add btn_click as a subview of you content view. Now button click action should work.

Resources