I've been trying to use a UILongPressGestureRecognizer in a MKAnnotationView subclass. Interestingly the gesture recognizer only triggers when using two ore fingers/touches.
What prevents the gesture recognizer to get triggered with only one touch?
Implementation
UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(handleLongPress:)];
pressRecognizer.minimumPressDuration = 0.25;
pressRecognizer.numberOfTapsRequired = 0;
pressRecognizer.numberOfTouchesRequired = 1;
The same implementation in a normal UIView shows the expected behaviour working with one touch. Yet it's possible to use touchesBegan: and touchesEnded: to get a long press gesture working I'm still curious what the reason for this is.
Have you seen this question ?
For using my UILongPressGestureRecognizer I disabled AnnotationView and added GestureRecognizer to it:
[ann_view setEnabled:NO];
UILongPressGestureRecognizer* long_press = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressAnnotationView:)];
long_press.minimumPressDuration = 1.5;
[ann_view addGestureRecognizer:long_press];
[long_press release];
Related
I'm trying to detecting a two finger swipe touch and use it like the touch board in Mac(two finger up and two finger down listening) and the code like;
UISwipeGestureRecognizer *swipeUpTwoFinger = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeTwoFinger:)];
swipeUpTwoFinger.numberOfTouchesRequired = 2;
swipeUpTwoFinger.direction = UISwipeGestureRecognizerDirectionUp;
[self.view addGestureRecognizer:swipeUpTwoFinger];
UISwipeGestureRecognizer *swipeDownTwoFinger = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeTwoFinger:)];
swipeDownTwoFinger.numberOfTouchesRequired = 2;
swipeDownTwoFinger.direction = UISwipeGestureRecognizerDirectionDown;
[self.view addGestureRecognizer:swipeDownTwoFinger];
-(void)handleSwipeTwoFinger:(UISwipeGestureRecognizer *)swipeGestureRecognizer
{
if (swipeGestureRecognizer.direction == UISwipeGestureRecognizerDirectionUp) {
NSLog(#"handleSwipeUpTwoFinger");
}
else if (swipeGestureRecognizer.direction == UISwipeGestureRecognizerDirectionDown) {
NSLog(#"handleSwipeDownTwoFinger");
}
}
The Problem is
I could get a Up or Down Swipe when I touch,but just one event whatever I do the Up Down swipe under one pressing.
What I want is like the touch board in Mac,when I swipe up it listened and without leaving the board swipe down it could listened too.
The Question is
How could I do this with UISwipeGestureRecognizer?
Or it have limitation couldn't do what I want should I do it with UIPanGestureRecognizer or listening various touch event do logic with myself? or any other implementation ? any demo?
any suggestions would be appreciated :)
I'm working on a GLKViewController based game which interprets taps and swipes as game controls. I want to support two-player mode by letting the first player tap or swipe on the left side of the screen, and letting the second player tap or swipe on the right side of the screen. In a perfect world, I'd like the gesture recognizers to work even if the swipes are sloppy and go past the centerline of the screen (with the starting point of the swipe being used to determine which player gets the input).
What would be the best way to implement this? Can I lay down a gesture recognizer on the left half of the screen, and another one on the right side of the screen? Will two separate recognizers work properly together even if both sides are being tapped/swiped rapidly at the same time? Or should I create a full-screen recognizer and parse the swipes and taps entirely on my own? I don't have experience with gesture recognizers so I don't know what the preferred approach is or how well they work when you have more than one being swiped on simultaneously.
I ended up making two UIViews overlaid on top of my GLKView, one on the left side of the screen and one on the right. Each view has a UIPanGestureRecognizer and a UILongPressGestureRecognizer (the long-press recognizer is basically a more flexible tap—I needed to use it to reject some gestures from being interpreted both as a pan and a tap at the same time). This worked magnificantly.
- (void)viewDidLoad
{
[super viewDidLoad];
// Add tap and pan gesture recognizers to handle game input.
{
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleLeftSidePan:)];
panRecognizer.delegate = self;
[self.leftSideView addGestureRecognizer:panRecognizer];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLeftSideLongPress:)];
longPressRecognizer.delegate = self;
longPressRecognizer.minimumPressDuration = 0.0;
[self.leftSideView addGestureRecognizer:longPressRecognizer];
}
{
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleRightSidePan:)];
panRecognizer.delegate = self;
[self.rightSideView addGestureRecognizer:panRecognizer];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleRightSideLongPress:)];
longPressRecognizer.delegate = self;
longPressRecognizer.minimumPressDuration = 0.0;
[self.rightSideView addGestureRecognizer:longPressRecognizer];
}
}
- (void)handleLeftSidePan:(UIPanGestureRecognizer *)panRecognizer
{
[self handleGameScreenPan:panRecognizer withVirtualController:&g_iPadVirtualController[0]];
}
- (void)handleRightSidePan:(UIPanGestureRecognizer *)panRecognizer
{
[self handleGameScreenPan:panRecognizer withVirtualController:&g_iPadVirtualController[1]];
}
- (void)handleLeftSideLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
{
[self handleGameScreenLongPress:longPressRecognizer withVirtualController:&g_iPadVirtualController[0]];
}
- (void)handleRightSideLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
{
[self handleGameScreenLongPress:longPressRecognizer withVirtualController:&g_iPadVirtualController[1]];
}
I have already tried by this code but gesture not detect.
UISwipeGestureRecognizer *mSwipeUpRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(up_downBtn_waytosalon)];
[mSwipeUpRecognizer setDirection:UISwipeGestureRecognizerDirectionUp];
[self.duration_distanceView addGestureRecognizer:mSwipeUpRecognizer];
First Off, you need to always pass the gesture recognizer to your selector, otherwise you can't determine the state of your gesture.
Other than that first check if that view has userInteractionEnabled to YES, I'm guessing that view doesn't get any user interaction.
YOUR duration_distanceView view is imageView then add below line your code.
self.duration_distanceView.userInteractionEnabled = YES;
Try this code :
UISwipeGestureRecognizer *mSwipeUpRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(up_downBtn_waytosalon)];
self.duration_distanceView.userInteractionEnabled = YES;
[mSwipeUpRecognizer setDirection:UISwipeGestureRecognizerDirectionUp];
[self.duration_distanceView addGestureRecognizer:mSwipeUpRecognizer];
Initialize swipe gesture like this:
UISwipeGestureRecognizer *mSwipeUpRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(up_downBtn_waytosalon:)];
mSwipeUpRecognizer.direction = (UISwipeGestureRecognizerDirectionUp);
[self.duration_distanceView addGestureRecognizer:mSwipeUpRecognizer];
Implement the gesture action like this:
- (void) up_downBtn_waytosalon:(UISwipeGestureRecognizer *)sender {
if ( sender.direction | UISwipeGestureRecognizerDirectionUp )
NSLog(#"Its swipe up");
}
Hope this code helps.
In my mapview I am using the long press gesture to draw an MKCircle on the map. I'd like to use a double tap gesture to remove the circle. I add double tap as a gesture recognizer and it works correctly, however, while it removes the circle it also zooms a bit each time. I am wondering if there is a way to remove the default zoom by double tap behavior leaving just my own? I don't want to disable zooming for the whole map just when doing a double tap.
UILongPressGestureRecognizer *longGesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(getMapCoordinateFromTouch:)];
[self.mapView addGestureRecognizer:longGesture ];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(removeBoundary:)];
[tapGesture setNumberOfTapsRequired:2];
[tapGesture setNumberOfTouchesRequired:1];
tapGesture.delegate = self;
[self.mapView addGestureRecognizer:tapGesture];
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
-(void)removeBoundary:(UITapGestureRecognizer *)gesture
{
[self.mapView removeOverlays:self.mapView.overlays];
}
Additional Info:
It was suggested that this question was similar to: Disable double tap zoom in MKMapView (iOS 6)
However, that person was trying to disable all double taps and not just the default behavior. I did find some code in that question that I thought may help here but it does not. In that thread it was indicated that you could loop through the mapview and remove the gesture recognizer. This seems to work for gesture recognizers that I may add but it does not find any of the Apple default behaviors. I run the following code after viewWillAppear (I also tried viewDidAppear) but a break point shows that "gestures" is nil. So for some reason the view does not have Apple's default gestures.
NSArray *gestures = [self.mapView gestureRecognizers];
for (UIGestureRecognizer *recognizer in gestures)
{
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]])
{
UITapGestureRecognizer *tap = (UITapGestureRecognizer *)recognizer;
if (tap.numberOfTapsRequired == 2)
[self.mapView removeGestureRecognizer:recognizer];
}
}
Default UIGestureRecognizers appear to be added to the first subview, just remove them:
Example to remove all UITapGestureRecognizers (go from 13 to 9)
Swift:
print("GestureRecognizers before \(mainMap.subviews[0].gestureRecognizers?.count)")
if (mainMap.subviews[0].gestureRecognizers != nil){
for gesture in mainMap.subviews[0].gestureRecognizers!{
if (gesture.isKindOfClass(UITapGestureRecognizer)){
mainMap.subviews[0].removeGestureRecognizer(gesture)
}
}
}
print("GestureRecognizers after \(mainMap.subviews[0].gestureRecognizers?.count)")
Objective-C:
NSArray *gestures = [self.mapView.subviews.firstObject gestureRecognizers];
for (UIGestureRecognizer *recognizer in gestures)
{
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]])
{
UITapGestureRecognizer *tap = (UITapGestureRecognizer *)recognizer;
if (tap.numberOfTapsRequired == 2)
[self.mapView.subviews.firstObject removeGestureRecognizer:recognizer];
}
}
Try returning NO from -gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:. It sounds like both your recognizer and the map view's recognizer are acting on the double tap. You really want yours to get first crack at the gesture, so it can effectively override the map view's.
In my app I have added the new Gesture Recognizers that are available in the 3.2 SDK. Everything appears to be working correctly and the response time on the screen been very fast. But for some reason when I add requireGestureRecognizerToFail to some of my gestures, there is a very visible delay when the gesture is triggered. Below is a snippet of the code that I use to create the Gesture Recognizers. Does anyone know why there is a delay and how I can fix it? I'm using requireGestureRecognizerToFail to prevent the SingleTap gesture from triggering when the user performs a DoubleTap.
- (void)createGestureRecognizers {
//Single Finger Double-Tap
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleDoubleTap:)];
singleFingerDTap.numberOfTapsRequired = 2;
[super addGestureRecognizer:singleFingerDTap];
//Single Finger Tap
UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleTap:)];
singleFingerTap.numberOfTapsRequired = 1;
[singleFingerTap requireGestureRecognizerToFail:singleFingerDTap];
[self addGestureRecognizer:singleFingerTap];
//Two Finger Pan
UIPanGestureRecognizer *panGesture2 = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePanGesture2:)];
panGesture2.maximumNumberOfTouches = 2;
[super addGestureRecognizer:panGesture2];
//Single Finger Pan
UIPanGestureRecognizer *panGesture1 = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePanGesture1:)];
panGesture1.maximumNumberOfTouches = 1;
[panGesture1 requireGestureRecognizerToFail:panGesture2];
[super addGestureRecognizer:panGesture1];
[singleFingerDTap release];
[singleFingerTap release];
[panGesture1 release];
[panGesture2 release];
}
If you want to distinguish between a single and double tap, you must wait long enough to figure out that no second tap is coming before you can call it a single tap. The alternative would be to design all your single tap actions in such a way that they can asynchronously be canceled or reverted when a double tap is detected.
For example, if you have a single tap change pages and a double tap zoom, then you would have to animate a page changing on single tap, then reverse the animation and zoom instead when a second tap is detected. By then the view that handled the single tap may have moved. In most cases, that is more trouble and confusion then it is worth.