Adding single and double tap gesture on subview of UITableViewCell - iphone

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;

Related

How to pause/disable UILongPressGestureRecognizer on a UITableViewCell while in editmode

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

UITextView Subclass tap to didSelectRowAtIndexPath

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.

UIImageView showing UIPopOver

I would like to fire up this event:
- (IBAction)profilePop:(id)sender
{
ProfileViewController * profile = [[ProfileViewController alloc] init];
UIImageView * temp = ((UIImageView *)sender);
profile.uid = [[[posts objectAtIndex:((UIImageView *)sender).tag] creator] mid];
NSLog(#"profile id %#", profile.uid);
UIPopoverController * profilePop = [[UIPopoverController alloc] initWithContentViewController:profile];
[profilePop presentPopoverFromRect:CGRectMake(temp.frame.origin.x+temp.frame.size.width, temp.frame.origin.y + temp.frame.size.height/2, profile.view.frame.size.width, profile.view.frame.size.height) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
}
when a user taps on an UIImageView. All I am trying to do is to show a popover when an UIImageView is clicked and it is shown to the right of the UIImageView. I see that UIImageView doesn't have an addAction attribute from it as it's not a subclass of UIControl. I did some research that I might probably have to use a UIButton instead. Is this true? Is there a way to do this using UIImageView so I don't have to rewrite the code again? I
First.
you could get the touch on any object, which has super class as UIView.
if you see the UIImageView in apple documentation.
UIView : UIResponder : NSObject
UIResponder has function to get the touches. So implement the below functions in your view class and detect the touches on your UIImageView .
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
Second:
you could also create the UITapGestureRecognizer for UIImageView.
Check the below blog tutorial.
Working with UIGestureRecognizers
EDITED:
Use below code :
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[tapRecognizer setNumberOfTapsRequired:1];
[tapRecognizer setDelegate:self];
[MyImageView addGestureRecognizer:tapRecognizer];
if user tap once, tapped function will be called , So you tabbed function implemntation should be look like below
-(void)tapped:(id)sender
{
NSLog(#"See a tap gesture");
ProfileViewController * profile = [[ProfileViewController alloc] init];
UIImageView * temp = [(UIPanGestureRecognizer*)sender view];
profile.uid = [[[posts objectAtIndex:((UIImageView *)sender).tag] creator] mid];
NSLog(#"profile id %#", profile.uid);
UIPopoverController * profilePop = [[UIPopoverController alloc] initWithContentViewController:profile];
[profilePop presentPopoverFromRect:CGRectMake(temp.frame.origin.x+temp.frame.size.width, temp.frame.origin.y + temp.frame.size.height/2, profile.view.frame.size.width, profile.view.frame.size.height) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
}
What about using an UIButton instead and using the class method to add your UIImage to it.
- (void)setImage:(UIImage *)image forState:(UIControlState)state
You can define a UIButton of type custom, don't give it any text or image, give it a clear background, and the same frame as your image view. Then add your target/selector to the button and it will appear to the user they are tapping the image view, when they are actually tapping an invisible button. This should take about 3 minutes to set up in IB, so I don't think you'll need to rewrite your code again.
You could create a Tap Gesture recognizer and attach it to the UIImageView.
// By now imageView exists!
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapGesture:)];
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[imageView addGestureRecognizer:tapGesture];
[tapGesture release];
Now the handleTapGesture will be quite similar to your -profilePop:
Since you are talking of pop over controllers, I assume you already have gesture recognizers available.

Is it possible to receive UITapGestureRecognizer calls in 2 classes at the same time

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.

Do I need to release a gesture recognizer?

If I add a gesture recognizer to a table cell called cell, e.g.:
UILongPressGestureRecognizer *_longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(cellGestureRecognized:)];
_longPressRecognizer.allowableMovement = 20;
_longPressRecognizer.minimumPressDuration = 1.0f;
[cell addGestureRecognizer:_longPressRecognizer];
[_longPressRecognizer release], _longPressRecognizer = nil;
Do I need to manually call -removeGestureRecognizer: on this cell at some point, or does the gesture recognizer get removed and released for me when the cell is no longer used?
The gesture recognizers are added to an internal NSMutableArray of the view. This array will be released once the view is deallocated. Thus -removeGestureRecognizer: doesn't need to be called manually.