touchesBegan not working on UITableView - ios

I have UITableView over the full screen. What I would like to know is to find the location where I clicked a cell.
What I want to do is to show the copy option when any cell is clicked.
For that I tried
- (void) touchesEnded:(NSSet *)touches
withEvent:(UIEvent *)event {
//some code
}
but this method is not getting called.
Any idea how can I find user touch over UITableView

if you just want to show Copy option when any cell is clicked then you have to go for
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
method of tableView. there is no need to use Touch event.
Using this method you can get the copy of selected cell using indexPath.
Edit
To get the position of selected cell you can use rectForRowAtIndexPath method
CGRect rectInTableView = [tableView rectForRowAtIndexPath:indexPath];
Edit2
CGRect rectInSuperview = [tableView convertRect:rectInTableView toView:[tableView superview]];

You cannot detect the touch location on a UITableView. For detecting there are two options for you.
1. Either subclass your `UITableView`
OR
2. Add a `UIPanGesture` explicitly in the view.
UITableView inherits the property of pan gesture and UIScrollView by default. Hence by subclassing it you can override the gesture methods and detect UITouchEvents and on the basis of your location you can show the copy option. If you will add a UIPanGesture on your UITableView then you have to add this method in your UIViewController and detect the UIPanGesture touch events.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
and your UIPanGesture selector method as below:
-(void) slideView: (UIPanGestureRecognizer *) recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
CGPoint touchLocation =[recognizer locationInView:self.yourTableViewReference];
//Your Rest Of The Code.
break;
case UIGestureRecognizerStateChanged:
break;
case UIGestureRecognizerStateEnded:
break;
default:
break;
}
}
Hope it helps you.

In case of UIScrollview or UITableView, touches methods does not get triggered. Please read more on Responder Chain.
To get the required output you have to sub class UITableView and UITableCellView and override [hitTest:withEvent:] and [pointInside:withEvent:] to get the CGPoint in the respective view.
You can read more here.

Create custom uitableview with hidden copy button and implement the delegate method didSelectRowAtIndexPath and then on the basis of row selection unhide the copy button.

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

Unable to select rows of UITableView in UITableViewController

I have created an app using a storyboard. In other screens, I have used a UITableViewController directly and the selection is working as expected.
In this case, I have a UITableView that is one of several controls within a UIViewController.
My custom ViewController.h file has a definition similar to the below:
#interface MyViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
#property (weak,nonatomic) IBOutlet UITableView *myTableView;
#end
Then within viewDidLoad I am doing this:
_myTableView.delegate = self;
_myTableView.dataSource = self;
Having done this, my numberOfSectionsInTableView, numberOfRowsInSection and cellForRowAtIndexPath methods are all being called and my table looks as I want it to.
The problem I have is that the rows do not select and the didSelectRowAtIndexPath method is not getting called.
I have checked that Selection is set to Single Selection in the storyboard view and I have also tried to set _myTableView.allowsSelection=YES; in viewDidLoad but this doesn't seem to make any difference.
I know that this is probably something to do with the fact that my table is within a normal view controller, but I can't figure out the magic step I've missed to make the selection work.
For now I have added a workaround. In cellForRowAtIndexPath I have attached a UITapGestureRecognizer to each view in the cell:
for(UIView *view in cell.contentView.subviews){
[view setUserInteractionEnabled:YES];
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRow:)];
[tap setNumberOfTapsRequired:1];
[view addGestureRecognizer:tap];
view.tag = row; // So that I can identify in the handler which row has been tapped
}
Then in the handler:
-(void)tapRow:(id)sender{
UITapGestureRecognizer *gesture = (UITapGestureRecognizer*)sender;
UIView *myView = (UIView*)gesture.view;
int row = myView.tag;
// Handle tap of row here
}
This achieves what I need, but I would still like to figure out what I've done wrong with the row selection!
Make sure there is not another view on top of the table view (e.g. a transparent view that would be stealing touch events). Also ensure that userInteraction is enabled for the tableview and all of its parent views.
We type sometime wrong delegate call didDeselectRowAtIndexPath instead of didSeselectRowAtIndexPath. Is it your case?
I see that you can update your content in the tableview so the connection should be OK. I think about mistakes with function names.
Just to test: Replace your didSelectRowAtIndexPath function with this (copy, paste).
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Selected %d",indexPath.row);
}
Just incase anyone still has this problem. I just realised -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method won't work for any view or subview with a tap gesture recogniser set to it. So remove the tap gesture recogniser from that view of it's parent view. if you still want to handle touch events, then use "touchesBegan" function. ` - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
}`
I had the exactly the same problem with my current project for two minutes yesterday when I realised that I didn't link my delegate and dataSource of the table view. You made it programmatically, so it should work. For me was a delegation problem, you can check your Attributes Inspector to make sure that everything is ok.
In cellForRowAtIndexPath method add this line
cell.userInteractionEnabled = NO;

Detecting touch inside UITableViewCell subview

I am unclear where I should add the UIGestureRecognizer code to corresponding subviews of a UITableViewCell. I have read all the related questions I could find. Right now my cells and cell's subviewsare generated inside of cellForRowAtIndexPath. I have tried to add the Gesture inside of cellForRowAtIndexPath with this:
UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[mySubview addGestureRecognizer:tapGesture];
tapGesture.cancelsTouchesInView = YES;
tapGesture.delegate = self;
However, this detects nothing. To verify my UIGesture recognizer is working I have used the above code on the tableView itself, and it does register touches as expected. Furthermore, when the tableView has the above gesture attached the below code is also being called as expected:
-(BOOL) gestureRecognizer:(UITapGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
NSLog(#"shouldRevceiveTouch");
return YES;
}
- (BOOL)gestureRecognizer:(UITapGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UITapGestureRecognizer *)otherGestureRecognizer
{
NSLog(#"simultaneously");
return YES;
}
I have tried to remove the GestureRecognizer from the tableView and inside of cellForRowAtIndexPath I have tried to attach the GestureRecognizer to the cell itself, any of its subviews, nothing else gets a touch detected. (None of the above code is triggered)
Clearly I am adding the GestureRecognizer incorrectly. Where/When would be an appropriate location/time to add the GestureRecognizer?
Thank you.
I've done similar thing, but it was UILongPressGestureRecognizer. I think there is no big difference (because all touches are received by UITableView). I've added gesture recognizer in controllers viewDidLoad method (NOT IN cell).
- (void) tableViewLongPress:(UILongPressGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.messageTableView];
NSIndexPath *indexPath = [self.messageTableView indexPathForRowAtPoint:p];
if (indexPath == nil)
NSLog(#"long press on table view but not on a row");
else {
UITableViewCell *cell = [self.messageTableView cellForRowAtIndexPath:indexPath];
CGPoint pointInCell = [cell convertPoint:p fromView:self.messageTableView];
}
}
You can change Long press to regular one and try it yourself
I needed to detect touches on different subviews inside my cell. also handling iOS 9's UITableViewCellContentView.
First I overrided touchesBegan inside the my custom UITableViewCell
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:touch.view];
// Imagine I have 2 labels inside my cell
CGPoint convertedPoint = [self.firstLabel convertPoint:point fromView:touch.view];
if ([self.firstLabel pointInside:convertedPoint withEvent:nil]) {
// Touched first label
return;
}
convertedPoint = [self.secondLabel convertPoint:point fromView:touch.view];
if ([self.secondLabel pointInside:convertedPoint withEvent:nil]) {
// Touched second label
return;
}
// no labels touched, call super which will call didSelectRowAtIndexPath
[super touchesBegan:touches withEvent:event];
}
And to fix support in iOS 9 we should override awakeFromNib or just disable the cell user intercations somehwere else if cell is not in Storyboard / xib:
- (void)awakeFromNib {
// Initialization code
self.contentView.userInteractionEnabled = NO;
}
of course we shouldn't forget to set our label user interactions enabled.
Not sure exactly what you are trying to do. If you just want to detect if the user taps on a cell within the table then you don't need to implement a gesture recognizer. Just implement the delegate method below to detect when a row from the table has been selected then process the elements of the row such as getting the subview, etc.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Do all my cool tap related stuff here for example, get the row that was tapped:
UITableViewCell *cell= [tableView cellForRowAtIndexPath:indexPath];
// get your subview (assume its a UIImageView) from cell - one way to do it below
UIImageView photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
}
If you describe your problem a little further then perhaps I can offer additional suggestions.

SWRevealViewController and TableView - swipe to delete not working

I added the SWRevealViewController to my app, along with the hamburger stack to access my menu. My app has a UITableView for the main application view. I want to allow users to delete items from the list using the swipe to delete function.
I enabled the swipe to delete and added a method call to handle this. I noticed that the method never gets called. Not sure how to get this to work.
Any help would be greatly appreciated.
I was asked for a code example. I have not made any changes to the SWRevealViewController standard source code. In my code for the TableView, I have added the following:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
Normally, the code above should show a delete button when you do a right to left swipe. However, this is not happening. My guess is that the SWRevealViewController is eating the pan gesture recognizer.
Just found a solution after reading a thread of a guy asking a similar question to the maker of this class. Whatever class you use to add the gesture recognizers, make it the delegate of the SWRevealController then paste in this method.
#pragma mark - SWRevealViewControllerDelegate
- (BOOL)revealControllerPanGestureShouldBegin:(SWRevealViewController *)revealController
{
float velocity = [revealController.panGestureRecognizer velocityInView:self.view].x;
if (velocity < 0 && self.revealViewController.frontViewPosition == FrontViewPositionLeft)
return NO;
else
return YES;
}
I have done like this < UIGestureRecognizerDelegate> in .h file in which you are implementing tableview cell delete functionality and in .m file viewDidLoad
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
self.revealViewController.panGestureRecognizer.delegate = self;
After that simply use this method
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([NSStringFromClass([touch.view class]) isEqualToString:#"UITableViewCellContentView"])
return NO;
else
return YES;
}

Why does the delegate call didSelectRowAtIndexPath when I tap an element that has its own handler?

I have a situation where elements in a UITableViewCell subclass have either tap gesture recognizers or UIButtons.
When I tap these, not only do its handlers get called, but so does the didSelectRowAtIndexPath: delegate method.
Now, ideally, if the user doesn't tap any of those elements, this should be passed on to the view underneath (i.e. the tableview or table cell) and be processed.
Why aren't these elements swallowing the touches and preventing the TableView delegate method from firing?
You need to override the following function..
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
Here you can check if the touch was initiated from a control of your choice and if that is the case you need to return NO to prevent default action from happening here .. and return YES if the earlier defined handler is to be called
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if(touch.view == self.playButton ) {
return NO;
}
return YES;
}
I "hacked" a solution which puts a tapGesture recognizer on the contentView of my UITableViewCell subclasses, whose delegate is my custom UITableViewController subclass. I use an empty didSelectRowAtIndexPath: implementation. The delegate method is
- (void)didSelectTableCell:(MyTableViewCell*)aCell;
And that did the job. Just need to remember when the cell is created to make sure I set the delegate.
Inelegant, but works fine.

Resources