Four-finger Multitasking gesture activates UIPinchGestureRecognizer gesture - ios

I am using a UIPinchGestureRecognizer, which uses 2 fingers by default. If a user decides to perform the multitask gesture, the pinch gestures action is also activated.
Is there a way to cancel the pinch gesture from occurring if more than four UITouch instances are detected?
Edit Removed sample code as it was the wrong approach.

Since you're not subclassing the UIPinchGestureRecognizer, you shouldn't be using touchBegan:withEvent:. Instead you should be handling it in the method that is called when a pinch occurs.
- (void)handlePinch:(UIPinchGestureRecognizer *)pinchGestureRecognizer
{
// if there are 2 fingers being used
if ([pinchGestureRecognizer numberOfTouches] == 2) {
// do stuff
}
}

With a multitask gesture, the numberOfTouches returned by the UIPinchGestureRecognizer is 2 instead of 4 or 5, because some touches are ignored.
You can subclass UIPinchGestureRecognizer and override ignoreTouch:forEvent to cancel the recognizer if the event has 4 or 5 touches:
- (void) ignoreTouch:(UITouch*)touch forEvent:(UIEvent*)event
{
[super ignoreTouch:touch forEvent:event];
// Cancel recognizer during a multitask gesture
if ([[event allTouches] count] > 3)
{
self.state = UIGestureRecognizerStateCancelled;
}
}

Related

how to recognize start and end of rotation gesture in iOS?

I need two functions which would be fired when the rotation gesture starts and finishes, because I need to know the whole angle of the rotation. Currently the gesture recogniser is fired all the time until the rotation finishes, and I cannot find out when it has finished, to find to total angle.
That's because the method you hook to your gesture gets called for all of the gestures states, like began/ended/canceled/changed. You can however ask the gesture for its current state within the method, and add specific functionality for these different states. Here's a basic example:
- (void)rotationGestureHandler:(UIRotationGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan) {
// do stuff - call method for gesture began
}else if (gesture.state == UIGestureRecognizerStateEnded) {
// do other stuff - call method for gesture ended
}
}

Periodic Event While Pan Gesture Hasn't Ended

So my current project has a pan gesture recognizer, and if I have panned to the top of the screen the screen it is supposed to scroll up to account for that gesture. While the gesture hasn't ended and the current position of the gesture remains at the top of the screen, I want to continually keep scrolling. My problem is the gesture recognizer only gets called when the state changes, therefore my content will only scroll if you move back and forth at the top, not continually while the gesture continues to be at the top. Is there any reasonable way to continually call code while the gesture hasn't ended, but isn't necessarily changing? Here is pseudo-code of what I have:
- (void)handleGestureRecognizer:(UIGestureRecognizer *)gesture {
if ( gesture.state == UIGestureRecognizerStateChanged ) {
CGPoint point = [gesture locationInView:self.view];
if (point.y < 100) {
//I would like this code to be called continually while the
//gesture hasn't ended, not necessarily only when it changes
[self updateScrollPosition];
}
}
I can think of a few ghetto ways to do it by setting state bools based on the current state of the recognizer and creating my own timer to periodically check, but it seems pretty hacky and I don't particularly like it, so I'm wondering if anyone could come up with a cleaner solution.
One way to use a timer and feel better about it would be to conceal it a little by using performSelector:withObject:afterDelay:
- (void)stillGesturing {
[self updateScrollPosition];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:#selector(stillGesturing) withObject:nil afterDelay:0.5];
}
// then in the recognizer target
if (gesture.state == UIGestureRecognizerStateEnded) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
} else if ( gesture.state == UIGestureRecognizerStateChanged ) {
CGPoint point = [gesture locationInView:self.view];
if (point.y < 100) {
//I would like this code to be called continually while the
//gesture hasn't ended, not necessarily only when it changes
[self stillGesturing];
}
}

pressGestureRecognizer and touchesBegan

I have the following problem.
I am using a UILongPressGestureRecognizer to put a UIView into a "toggle mode". If the UIView is in "toggle mode" the user is able to drag the UIView around the screen. For dragging the UIView around the screen I am using the methods touchesBegan, touchesMoved and touchesEnded.
It works, but: I have to lift my finger in order to drag it, because the touchesBegan method got already called and therefore is not called again and therefore I can't drag the UIView around the screen.
Is there any way to manually call touchesBegan after UILongPressGestureRecognizer got triggered (UILongPressGestureRecognizer changes a BOOL value and the touchesBegan only works if this BOOL is set to YES).
UILongPressGestureRecognizer is a continuous gesture recognizer, so rather than resorting to touchesMoved or UIPanGestureRecognizer, just check for UIGestureRecognizerStateChanged, e.g.:
- (void)viewDidLoad
{
[super viewDidLoad];
UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[self.view addGestureRecognizer:gesture];
}
- (void)handleGesture:(UILongPressGestureRecognizer *)gesture
{
CGPoint location = [gesture locationInView:gesture.view];
if (gesture.state == UIGestureRecognizerStateBegan)
{
// user held down their finger on the screen
// gesture started, entering the "toggle mode"
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
// user did not lift finger, but now proceeded to move finger
// do here whatever you wanted to do in the touchesMoved
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
// user lifted their finger
// all done, leaving the "toggle mode"
}
}
I would suggest you to use UIPanGestureRecognizer as it a recommended gesture for dragging.
You can configure the min. and max. number of touches required for a panning, using the following the properties:
maximumNumberOfTouches
minimumNumberOfTouches
You can handle the states like Began, Changed and Ended, like having animation for the required states.
Using the below method translate the point to the UIView in which you want it.
- (void)setTranslation:(CGPoint)translation inView:(UIView *)view
example:
You have to use a global variable to retain the old frame. Get this in UIGestureRecognizerStateBegan.
When the state is UIGestureRecognizerStateChanged. You can use the
-(void) pannningMyView:(UIPanGestureRecognizer*) panGesture{
if(panGesture.state==UIGestureRecognizerStateBegan){
//retain the original state
}else if(panGesture.state==UIGestureRecognizerStateChanged){
CGPoint translatedPoint=[panGesture translationInView:self.view];
//here you manage to get your new drag points.
}
}
Velocity of the drag. Based on the velocity you can provide a animation to show bouncing of a UIView
- (CGPoint)velocityInView:(UIView *)view

UIPinchGestureRecognizer not called

I have a single view on my iOS application, with a mapView in it.
When adding a tap or long press recognizer, the events are properly called.
But not with the pinch event...
UIPinchGestureRecognizer *handlePinchGesture=[[UIPinchGestureRecognizer alloc]initWithTarget:mapView action:#selector(handleGesture:)];
[mapView addGestureRecognizer:handlePinchGesture];
Any idea what I should add ?
Thanks.
Assuming your mapView is an MKMapView, it has its own pinch gesture recognizer for zooming the map.
If you want to add your own recognizer, you have to allow it to recognize simultaneously with the other (mapview-controlled) recognizer. Set your gesture recognizer's delegate and implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: (you can just always return YES).
You should also probably set self as the gesture recognizer's target and not the mapView.
In the handleGesture method did you do something like this:
CGFloat beginPinch; //declare this as your ivars
-(void)handleGesture:(UIPinchGestureRecognizer *)pinchRecognizer
{
if (pinchRecognizer.state == UIGestureRecognizerStateBegan)
{
beginPinch = pinchRecognizer.scale;
}
else if (pinchRecognizer.state == UIGestureRecognizerStateEnded)
{
if (pinchRecognizer.scale < beginPinch)
{
//do your stuff
}
}
}

How to avoid Touches cancelled event?

I have two views one beneath the another. I'm rotating the below view by touch sensing of top view. while trying to make a swipe, touches canceled event is called before touches ended event. While moving the finger touches began and touches moved events are called , and then touches ended event is called at the last(mostly). But sometimes while trying to move slowly,touches canceled event is called stopping the touch events to occur. So i couldn't rotate view in slow speed. What may be the problem? how to avoid touches canceled event?
Note: I'm drawing some graphs in views using core-plot lib.
If you are using any UIGestureRecognizers they automatically cancel touches to other views when they recognize their gesture. You can turn this behavior off with the cancelsTouchesInView property of the recognizer.
If your are not using UIGestureReconizer directlly, be aware of the property gestureRecognizers of the UITouch.
I have the same problem and with this code I solve it:
if (event.type == UIEventTypeTouches)
{
NSSet* tmpTouches = [event touchesForView:m_PhotoView];
if ([tmpTouches count] == 2)
{
UITouch *tmpTouch1 = [[tmpTouches allObjects] objectAtIndex:0];
UITouch *tmpTouch2 = [[tmpTouches allObjects] objectAtIndex:1];
if ((tmpTouch1 != nil)&&(tmpTouch2 != nil))
{
UIGestureRecognizer * tmpG;
if ([tmpTouch1.gestureRecognizers count] > 0)
{
tmpG = [tmpTouch1.gestureRecognizers objectAtIndex:0];
tmpG.cancelsTouchesInView = NO;
}
if ([tmpTouch2.gestureRecognizers count] > 0)
{
tmpG = [tmpTouch2.gestureRecognizers objectAtIndex:0];
tmpG.cancelsTouchesInView = NO;
}
// Code ...
}
}
}
Look out for UISwipeGestureRecognizer as well. This was causing the issue for me and is resolved once we set
[recognizer setCancelsTouchesInView:FALSE];
If a scroll view is involved, uncheck its property content touch Can Cancel On Scroll in the storybord.

Resources