I have a UITextView subclass that has an NSIndexPath property that is inside a UITableViewCell. When you tap on it, I'd like to be able to call didSelectRowAtIndexPath.
Here's what I have:
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:nil];
[theTextView addGestureRecognizer:singleFingerTap];
singleFingerTap.delegate = self;
....
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CustomTextView *theTextView = (CustomTextView *)touch.view;
NSLog(#"%#", theTextView.indexPath);
return YES;
}
I see from this question, I may even have to break up my didSelectRowAtIndexPath logic, which is fine. And I already know the index path of the view that was tapped.
What is the proper way to call this tableView method (or what didSelectRowAtIndexPath would do) from within the gesture recognizer method?
I answered a similar question here. Basically when you call:
[tableView selectRowAtIndexPath:path animated:YES scrollPosition:UITableViewScrollPositionNone];
the tableview doesn't fire the tableView:didSelectRowAtIndexPath: on the tableview's delegate, so in addition you are forced to directly call (And since you wrote the code in the delegate method there's no problem):
[tableView.delegate tableView:tableView didSelectRowAtIndexPath:path];
Also your passing nil for the action is really not how gesture-recognizers where mean to work:
[[UITapGestureRecognizer alloc] initWithTarget:self action:nil];
Generally you would do something like this:
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRecognized:)];
That basically tells the gesture recognizer to call the tapRecognized: method when it receives a tap. So basically to wrap it all up (I'm assuming this is in a UITableViewController or an object with a property named tableView as UITableViewControllers have by default).
-(void)tapRecognized:(UITapGestureRecognizer *)tapGR{
// there was a tap
CustomTextView *theTextView = (CustomTextView *)tapGR.view;
NSIndexPath *path = theTextView.indexPath;
[self.tableView selectRowAtIndexPath:path animated:YES scrollPosition:UITableViewScrollPositionNone];
[self.tableView.delegate tableView:self.tableView didSelectRowAtIndexPath:path];
}
If you need to do X both in response to a gesture and to a tap on a table view row, simply create a method X and call that from your gesture recognizer method as well as from your didSelectRowAtIndexPath table view delegate method.
Of course, theoretically,
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
would work, but I think calling the delegate method yourself is bad style to say the least.
Related
I have a UIImageView in UITableViewCell. I am trying to add Single Tap gesture to view it in full size and double tap gesture to like. Here is my code for that:
UITapGestureRecognizer *aSingleTapGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SingleTapGestureRecognizer:)] autorelease];
aSingleTapGesture.delegate = self;
aSingleTapGesture.numberOfTapsRequired = 1;
[cell.imgProductImage addGestureRecognizer:aSingleTapGesture];
UITapGestureRecognizer *aDoubleTapGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapGestureRecognizer:)] autorelease];
aDoubleTapGesture.delegate = self;
aDoubleTapGesture.numberOfTapsRequired = 2;
[cell.imgProductImage addGestureRecognizer:aDoubleTapGesture];
It only calls single tap gesture and double tap gesture was never being called. Than I used require to fail on single tap gesture.
[aSingleTapGesture requireGestureRecognizerToFail:aDoubleTapGesture];
Well, using this code I am able to call single and double tap gesture both, But the problem is the tableView:didSelectRowAtIndexPath: is also being called. I don't want to call didSelectRowAtIndexPath if my gestures are being called. As of my understanding it is being called because single tap gesture is waiting for double tap gesture mean while didSelectRowAtIndexPath is being called.
Any help please.
You can use UIButton instead of UIImageView in tableview cell. Use following code for getting single and double tap.
//add target for your UITableViewCell's button
[aBtnThumbObj addTarget:self action:#selector(btnThumbClickDouble:) forControlEvents:UIControlEventTouchDownRepeat];
[aBtnThumbObj addTarget:self action:#selector(btnThumbClickSingle:) forControlEvents:UIControlEventTouchUpInside];
-(void)btnThumbClickSingle:(UIButton*)sender
{
[self performSelector:#selector(singleTapOnButtonSelector:) withObject:sender afterDelay:0.2];
}
-(void)btnThumbClickDouble:(UIButton*)sender
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(singleTapOnButtonSelector:) object:sender];
//Your implementation for double tap
}
-(void)singleTapOnButtonSelector:(UIButton*)sender
{
//Your implementation for single tap
}
You can set selection style for your cell as
cell.selectionStyle = UITableViewCellSelectionStyleNone;
And then just don't implement your -tableViewDidSelectRowAtIndexPath method
These two lines of code work for me:
tapGesture.cancelsTouchesInView = YES;
tapGesture.delaysTouchesBegan = YES;
i am making a chat application, and in the chat window there are uilabels in uitableviewcells. initially the keyboard will be present but when the user touches on any place on uitableview, i will make the chat window as fullscreen (dissappearing keyboard).
i cant find a way/trick to accomplish this.
i have tried the following method: by using tableview:didselectrowatindexpath, i am able to do it but, user needs to press on an existent uitableviewcell. but i want to understand the press even when uitableview is empty..
note: my chat tableview is interactive e.x. some rows will include image button which need to be pressable, so i cant just put an invisible button onto uitableview.
thank you for your thoughts
Aytunc Isseven
What you want to do is add a gesture recognizer to the UITableView that responds to the appropriate gestures. I would recommend against using UITapGestureRecognizer as the UITableView is already using taps for selecting the cells, so you might want to try the UILongPressGestureRecognizer. I put together a small sample of how you can do this as follows:
In my viewDidLoad I did the following:
UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressFrom:)];
[self.tableView addGestureRecognizer:gesture];
and the handleLongPressFrom: is as follows:
- (void)handleLongPressFrom:(UILongPressGestureRecognizer *)recognizer {
NSLog(#"handleLongPressFrom: %#", recognizer);
// Add real code here
}
The full list of gestures can be found here.
Oh, if you did want to still use tap, check out this stack overflow question. I don't know if the method presented works fully, but it'd be a good place to start.
Using UITapGestureRecognizer with a UITableView:
Okay, since the tap gesture seems to be the correct one for your use case you can try and do the following. Step 1 is to set up the gesture recognizer as I listed above using the tap gesture instead of the long press gesture.
The code in viewDidLoad is very similar with an important addition ...
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFrom:)];
// we need to set the gesture delegate so we can allow the tap to pass through to the
// UITableViewCell if necessary.
gesture.delegate = self;
[self.tableView addGestureRecognizer:gesture];
The handleTapFrom: function is pretty much the same with just the different gesture recognizer as the parameter.
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {
NSLog(#"handleTapFrom: %#", recognizer);
// Add real code here
}
The major changes to this approach is that we need to implement the UIGestureRecognizerDelegate protocol. Since our goal is to allow the tap gesture to pass through the UITableView to it's subviews (i.e. the UITableViewCell and it's components) we need to implement the gestureRecognizer:shouldRecieveTouch: function. The following implementation should cover what you are attempting.
#pragma mark UIGestureRecognizerDelegate methods
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// If the view that is touched is not the view associated with this view's table view, but
// is one of the sub-views, we should not recognize the touch.
if (touch.view != self.tableView && [touch.view isDescendantOfView:self.tableView]) {
return NO;
}
return YES;
}
The isDescendantOfView: function returns YES if the view it is testing against is the same as the view doing the testing, so we need to accommodate that case separately. You can generify this function by using gestureRecognizer.view instead of self.tableView, but I didn't think it was necessary in this case.
The trick is to make your viewController put a tap recognizer on the view but make it always opt out by returning NO from the delegate method "gestureRecognizerShouldBegin". That way gestureRecognizerShouldBegin gets called for every touch on the view, but you don't interfere with the normal event handling of the table.
- (void)viewDidLoad {
[super viewDidLoad];
[self detectTouchesOnView:self.tableView];
}
- (void)detectTouchesOnView:(UIView*)theView {
UITapGestureRecognizer* tapR = [[UITapGestureRecognizer alloc]initWithTarget:nil action:nil];
tapR.delegate = self;
[theView addGestureRecognizer:tapR];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
// React to the UITableView being touched E.G. by hiding the keyboard as below.
[self.view endEditing:YES];
return NO;
}
My UIViewController hierarchy is as follows
UIView
UIScrollView
UITableView
UITableViewCell
UITextField
The UITableView is added to the view controller programmatically.
I want to hide keyboard when user taps outside UTTextField either on the view or on the UITableView
I am executing some methods when user taps on other UITableView rows
I tried
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
UIScrollView doesn't send the touch events.
I tried adding Tap gesture
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[singleTap setNumberOfTapsRequired:1];
[[self view] addGestureRecognizer:singleTap];
but with TapGesture, hides the following event
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
are there any other possible ways to hide the keyboard?
use the code : [self.view endEditing:YES];
use the UITextFieldDelegate
and the method
– textFieldShouldEndEditing:(UITextField*) txtField
{
[txtField resignKeyPads];
return YES:
}
this can also done by the scrolview delgate too
-(void) scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//resign all keypads of all textfields use array containing keypads
}
one thing more is change the class of UIView to UIControl and make a method IBAction and connect the UIControl touchupInside to that ibaction, it will resign keypads
If you want to still use tap gestures you need to add the gesture recogniser to the table background like so:
[tableView.backgroundView addGestureRecognizer:singleTap];
This will prevent the hiding of:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
If you want to put a gesture recognizer on the background view you need to make sure it has one.
Add
self.tableView.backgroundView = [[UIView alloc] initWithFrame:self.tableView.bounds];
UITableView didSelectRowAtIndexPath will not call when UITableview is Edit Mode. So you suppose to create custom gesture event to handle the same.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//cell design code goes here.
UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
doubleTapGestureRecognizer.numberOfTapsRequired = 1;
//tapGestureRecognizer.delegate = self;
[cell addGestureRecognizer:doubleTapGestureRecognizer];
return cell;
}
//Handle the click event
-(void) handleDoubleTap:(UITapGestureRecognizer*)sender{
[self.view endEditing:YES];
UITableViewCell * cell =(UITableViewCell*) sender.view;
//get the selected table indexpath.
NSIndexPath * indexPath= [tblCart indexPathForCell:cell]; //to handle the scroll
tblCart scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
NSLog(#"Comming");
}
In a tableview I have on every cell a UILongPressGestureRecognizer which I add like this:
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(TableCellLongPressed:)];
longPress.minimumPressDuration = 0.5f;
[cell addGestureRecognizer:longPress];
[longPress release];
Now I do have the following problem I want the user to be able to rearrange the cell in the tableview so I have a button that sets the tableView to EditMode like this:
[self.myTableView setEditing:!self.myTableView.editing animated:YES];
Now when the user tries to drag a cell and does not drag it far enough the longPress fires his action which is very annoying for the user cause another view gets pushed.
How can I pause or disable the UILongPressGestureRecognizer when the tableView is in EditMode?
You should implement the UIGestureRecognizerDelegate delegate for this method:
gestureRecognizer:shouldReceiveTouch:
In the method, check if you're editing the table and return NO if you are.
Tim
I want to call an action in two classes (a superview and a full screen subview) when the user single taps the screen. But, when I add a UITapGestureRecognizer to the subview, the one added to the superview is overridden. Is it possible to add a UITapGestureRecognizer to a subview without overriding the UITapGestureRecognizer added to the superview?
If so, how can I do this?
Thanks!
Edit:
From my main viewController "MyToolBerController", I'm adding the subview from another viewController as follows:
PhotoViewController *photoViewController = [[PhotoViewController alloc] initWithNibName:#"PhotoViewController" bundle:nil];
myPhotoView = photoViewController.view;
[self.view addSubview:myPhotoView];
I add the GestureRecognizer in the MyToolBerController like this:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapFrom:)];
[singleTap setNumberOfTapsRequired:1];
singleTap.delegate = self;
[myPhotoView addGestureRecognizer:singleTap];
[singleTap release];
This all works fine, but I need to call a method in the PhotoViewController class when the view is tapped as well as in the MyToolBerController class.
When I add another UITapGestureRecognizer in the photoViewController, it overrides the UITapGestureRecognizer added in the superView.
Gesture recognizers can dispatch multiple actions when the gesture occurs. You can add the subview as another target of the gesture recognizer and only use a single UITapGestureRecognizer instance:
[tapRecognizer addTarget:theSubview action:#selector(whatever:)];
In your gesture recognizer selector method, pass the information along to the subview. There's no need to have multiple gesture recognizers for the same gesture. Something like:
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *)sender
{
CGPoint tapPoint = [sender locationInView:sender.view.superview];
UIView *subview = [parentView viewWithTag:100];
[subview doSomethingWithPoint:tapPoint];
}
This of course means that your subview that needs to be notified should be given the tag 100 either in Interface Builder or in code when the view controller gets loaded.
Update based on Jonah's code:
So instead of retaining the view, retain the view controller:
PhotoViewController *photoViewController = [[PhotoViewController alloc] initWithNibName:#"PhotoViewController" bundle:nil];
self.myPhotoViewController = photoViewController;
Which means you need to declare it this way in the MyToolbarController header:
#property (nonatomic, retain) PhotoViewController *myPhotoViewController;
Then, when your gesture selector gets called, pass the message along to the view controller you retained. Something like:
- (IBAction)handleSingleTapFrom:(UIGestureRecognizer *)sender
{
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[myPhotoViewController doSomethingWithPoint:tapPoint];
}
Of course the -doSomethingWithPoint: method is only for example. You can name and create any method you want that takes any parameter you want to pass in your PhotoViewController.
Let me know if you need further clarification.