Adding different UITapGestureRecognizer to UIViews - ios

I've made a custom UIView that appears in my app in two places. It first appears in a regular view controller along with a text view, and then a tap gesture on the UIView moves to another view controller where the custom UIView is shown enlarged. I want to have a different tap gesture for the enlarged UIView, but when I tried putting this into the storyboard, the first tap gesture stopped working (since the new tap gesture was only reachable via the first tap gesture's segue to the new view controller, I wasn't able to see if it worked or not.
Does adding the second UITapGestureRecognizer to the view cause the first to become invalid? I don't see any other reason this would've caused an issue. If so, do I need to create separate classes for the two different custom UIViews? I tried to avoid this since they're showing the same thing just in different sizes but it seems the different tap gestures may be causing an issue.

Adding different UITapGesture in different.
View1
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapBehind1:)];
[recognizer setNumberOfTapsRequired:1];
recognizer.cancelsTouchesInView = NO;
[View1 addGestureRecognizer:recognizer];
View2
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapBehind2:)];
[recognizer setNumberOfTapsRequired:1];
recognizer.cancelsTouchesInView = NO;
[View2 addGestureRecognizer:recognizer];
View1 handle Tap method
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
[View1 removeGestureRecognizer:sender];
}
}
View2 handle Tap method
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
[View2 removeGestureRecognizer:sender];
}
}

Related

How to remove gesture for RESideMenu from UIView

I want to remove gesture recogniser for RESidemenu controller form particular UIView. how can we remove it.
i have removed all gestures from view, but its not working fine.
for (UIGestureRecognizer *gesture in self.view.gestureRecognizers)
{
[self.view removeGestureRecognizer:gesture];
}

button action gets called before double tap gesture method

I've a scrollview on which one button is added, now I want to give action as well as double tap gesture to the button.
If the button is on UIView, both action of the button and double tap gesture methods work perfectly. But if the button is present on UIScrollView then action gets called followed by double tap gesture method.
Any help will be appreciated.
The scroll view needs a fair amount of touch logic to track, and that must be getting confused with the button's touch logic and your gesture recognizer.
Without investigating that further, I'd get around this by handling the button's taps via two gesture recognizers that you control, which should work no matter the parent view.
Do not give the button a target/action as you normally would in IB or in code
Create a single tap gesture recognizer to handle the button action.
Create a double tap gr as you probably do already. Add both to the button.
// for your current target-action behavior
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap:)];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTap:)];
singleTap.numberOfTapsRequired = 1;
doubleTap.numberOfTapsRequired = 2;
[singleTap requireGestureRecognizerToFail:doubleTap];
[button addGestureRecognizer:singleTap];
[button addGestureRecognizer:doubleTap];
Then, instead of your IBAction method...
- (void)singleTap:(UIGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateRecognized) {
// target-action behavior here
NSLog(#"single tapped");
}
}
- (void)doubleTapped:(UIGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateRecognized) {
NSLog(#"double tapped");
}
}

iOS: Tap Recognizer not consistent

I have a scenario like shown below
Right now, I am showing only 1 view with label 8. But I am planning to add 3 more such views to the HolderView.
The SmallerView/s are created from other Nib files.
I did this code for adding Tap Recognizer for ViewController's view
UITapGestureRecognizer *tapRecognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapRecognized2:)];
[tapRecognizer setDelegate:self];
[self.view addGestureRecognizer:tapRecognizer];
Code for adding Tap Recognizer to the smaller views
I added smaller views to the HolderView. And assigned Tag IDs to them. After that,
for (SmallerView *view in HolderView.subviews) {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRecognized:)];
[recognizer setDelegate:self];
NSLog(#"Added gesture to the view with tag: %ld",view.tag);
[view addGestureRecognizer:recognizer];
}
3.
- (void)tapRecognized:(UITapGestureRecognizer *)paramSender
{
NSLog(#"tapped on %ld", paramSender.view.tag);
}
- (void)tapRecognized2:(UITapGestureRecognizer *)paramSender
{
NSLog(#"VC view");
}
I have enabled UserInteraction (both in code and Inspector) for all the views and UILabels on smaller views too.
The problem now is...
The smaller view's Tap recognisers are not really working consistently. Sometimes they print the output. All at suddenly it prints the ViewController's recogniser's output.
Please help
UPDATE:
Below is my View diagram.
Green Border: (In UIView's initWithFrame)d
self.layer.borderColor = [UIColor greenColor].CGColor;
Red Border:
MyTile *tile = [[[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil] objectAtIndex:0];
self.myLabel.layer.borderColor=[UIColor redColor].CGColor;
Why that Green Border is coming only of that size? Shouldn't that be full square?
And, the gesture works only when I tap on the green area. Why?
It seems that you have a tap gesture recognizer on a view and also a tap gesture recognizer on its superview. The way gesture recognizers work is that, by default, both of them will be candidates to recognize the tap. If that is not what you want, it is up to you to prevent it.
You can:
set up a "you-go-first" relationship between the gesture recognizers, or
you can use their delegates to help decide between them, or
you can set up the subview so that it stops the superview's gesture recognizer from recognizing.
You have lots of options!
But if you do none of them, then both this view's gesture recognizer and its superview's gesture recognizer will be trying to recognize.
Perhaps a better solution would just to add one tapGestureRecognizer to the parent view.
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
[tap addTarget:self action:#selector(handleTap:)];
[holderView addGestureRecognizer:tap];
Then add the target method
- (void) handleTap:(UITapGestureRecognizer *)tap {
UIView * holderView;
CGPoint tapPoint = [tap locationInView:holderView];
for (UIView * v in holderView.subviews) {
if (CGRectContainsPoint(v.frame, tapPoint)) {
// v is the subview that was pressed.
// add your code here.
break;
}
}
}
I have assigned a new frame to the frame which was having wrong borders. This helped in getting the gestures right.

Strikethrough Text with Animation iOS

What is the best way to display a strikethrough animation? When a user swipes their finger across a UITableViewCell, I would like to animate a thin line across cell.textlabel.text
The two ways I've thought of so far would be using Animation or somehow displaying a custom image and revealing it slowly from left to right? Does anybody have any advice on this?
I already have the swipe gestures working, I now just need to know how to make the animation happen:
Add Gesture Recognizer:
//Add a left swipe gesture recognizer
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeLeft:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
[self.tableView addGestureRecognizer:recognizer];
//Add a right swipe gesture recognizer
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeRight:)];
recognizer.delegate = self;
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[self.tableView addGestureRecognizer:recognizer];
Delegate Methods for Gestures:
- (void)handleSwipeLeft:(UISwipeGestureRecognizer *)gestureRecognizer
{
NSLog(#"uncompleted");
}
// Cross Item off of the list
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer
{
NSLog(#"completed");
}
Based on what you have so far the following should work:
when handleSwipeLeft (or right) fires, place a new UIView with a black background color over the textfield at the textfield's x point and around halfway to the y point with a width of 0 and height of 1
then, call [UIView animationWithDuration....] changing the UIView's width property to be roughly the width of the textfield.
This should be close to what you want with some tweaking. I don't think it will be possible to animate the strikethrough from using the properties of the font alone but this technique should simulate it just fine.
Good job on getting half way there.

How to respond only to a single-tap gesture while letting objects behind respond to double-taps?

I'm displaying a document in a UIWebView. I want to place a hotspot over the document to trigger an action when it is tapped, but I also want to maintain the default UIWebView behavior of auto-zooming the document when it is double-tapped. I can't figure out how to respond to the single-taps while letting the UIWebView respond to the double-taps.
I first set up the hotspot as a transparent UIButton with an action, but double-tapping the hotspot resulted in the hotspot action being called twice. So I removed the action from the button and attached a single-tap gesture instead:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapAction:)];
singleTap.numberOfTapsRequired = 1;
singleTap.delegate = self;
[self.hotspot addGestureRecognizer:singleTap];
[singleTap release];
This works the same as the normal button action. But then I created a double-tap gesture, and configured it to block the single-tap gesture with requireGestureRecognizerToFail:
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(zoomWebView:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
[self.hotspot addGestureRecognizer:doubleTap];
[doubleTap release];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapAction:)];
singleTap.numberOfTapsRequired = 1;
[singleTap requireGestureRecognizerToFail:doubleTap];
singleTap.delegate = self;
[self.hotspot addGestureRecognizer:singleTap];
[singleTap release];
- (void)zoomWebView:(UITapGestureRecognizer *)gesture {
NSLog(#"double tap");
}
With this setup, a single-tap on the hotspot calls singleTapAction and a double-tap on the hotspot calls zoomWebView (a custom method). This is good because singleTapAction is no longer called twice, but bad because the UIWebView no longer responds to the double-tap.
I tried forwarding the double-tap event from my doubleTap gesture to the UIWebView by subclassing UITapGestureRecognizer, overriding the touchesBegan and touchesEnded methods, and sending their arguments on to the corresponding methods of the UIWebView. When I did that, I could see that my subclass was receiving the events, but the UIWebView didn't respond to the forwarded events. This is to be expected because the Event Handling Guide for iOS says that we can only forward events to custom subclasses of UIView, not to UIKit framework objects.
Is there a way to prevent my single-tap gesture from responding to double-taps that doesn't divert the double-tap events? This seems like a basic requirement, but I can't see a straightforward way to do it. I read about and experimented with UIGestureRecognizer's touch delivery properties, but no combination of values stopped the single-tap gesture from consuming the double-tap gesture.
By the way, the relationship between the hotspot and the UIWebView in my view hierarchy is that of "cousins" -- they are subviews of two sibling views. If I add the gesture recognizers to hotspot view, the web view or their "grandparent" view, I get the same results.
Okay, I found a solution in two parts:
1) I had to add my gestures to a parent view of the UIWebView. Unmatched events don't travel through overlapping objects in a view from layer to layer as I was imagining. Instead, they travel through the hierarchy of views from child to parent. So as long as I was adding my double-tap gesture to a sibling or "cousin" view of the UIWebView, it was never going to proceed on to the web view. This means that I can't use buttons or views laid out in Interface Builder to determine multiple hotspot areas. Instead, I have to redirect all single-tap events to one method and then look at the touch positions to determine what action to trigger.
2) I had to add the gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: method to my view controller (the delegate of my gestures) and return YES. This allows my single-tap gesture to respond even when I'm displaying HTML, text or image content in the UIWebView, which implements its own gesture for these content types. I learned this part from this answer.
With these changes, I understand the hierarchy of events to be:
Single-tap web view: my single-tap gesture on the web view's parent view responds; the web view's single-tap gesture also responds if applicable because simultaneous gestures are enabled
Double-tap web view: my double-tap gesture on the web view's parent view responds (but doesn't do anything); my single-tap gesture does not respond because it is configured to only respond if the double-tap fails; the web view's double-tap event also responds because it is part of the view hierarchy (it seems that the double-tap functionality of the web view does not use a gesture because otherwise it would have take precedence over my own gesture in my original setup)
With that explanation out of the way, here's some working code:
- (void)viewDidLoad {
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapWebView:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
[self.webViewParent addGestureRecognizer:doubleTap];
[doubleTap release];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapWebView:)];
singleTap.numberOfTapsRequired = 1;
[singleTap requireGestureRecognizerToFail:doubleTap];
singleTap.delegate = self;
[self.webViewParent addGestureRecognizer:singleTap];
[singleTap release];
}
- (void)doubleTapWebView:(UITapGestureRecognizer *)gesture {
NSLog(#"double-tap");
// nothing to do here
}
- (void)singleTapWebView:(UITapGestureRecognizer *)gesture {
NSLog(#"single-tap");
CGPoint touchLocation = [gesture locationInView:self.webViewParent];
float x = touchLocation.x;
float y = touchLocation.y;
CGRect frame = self.webViewParent.frame;
if (y < frame.size.height * .33) {
NSLog(#"top");
} else if (y > frame.size.height * .67) {
NSLog(#"bottom");
} else if (x < frame.size.width * .33) {
NSLog(#"left");
} else if (x > frame.size.width * .67) {
NSLog(#"right");
} else {
NSLog(#"center");
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Always been a problem with mouse clicks since event handling began.
It's impossible at the instance of a single click, for any software to determine that a double click is about to happen.
So, if you want to handle single clicking and double clicking, you'll have to do your own double click handling.
Time the instances of single click, and generate a double click event of your own.
Hopefully that can transfer to tapping in your context.

Resources