Unpinch custom three-finger gesture recognizer in iOS

I want to create a custom three- finger gesture recognizer. This is similar to recognizing gesture recognition.

All I need is an idea on how to recognize it.

My gesture should recognize three fingers with three directions. For instance:

enter image description here

enter image description here

enter image description here

I hope the images make sense. I need to make it flexible for any three opposing directions. Thanks in advance. Any help would be appreciated.

I know about the methods of the subclass, and I created individual gestures with just one finger, like a semicircle, a full circle. I need a coding idea on how to handle this.

+6
source share
4 answers

A quick note for future readers: the way you unlock with three fingers adds the distances ab, bc, ac.

However, if your graphics package just has a “triangle area” on hand - just use it. ("It saves one whole line of code!")

Hope this helps.


All you have to do is keep track of:

distance between three fingers!

Just add "every" permutation

(Well, there are three .. ab, ac and cb. Just add them to have everything!)

When this value, say, triple from the initial value, that "outward triple failure."

... amazing that is simple.

Angles do not matter.


Footnote if you want to be a smartphone: this applies to any pinch / unclean gestures, 2, 3 fingers, whatever:

track the derivative of the sum-distance (I mean speed), not the distance. (Unusually, this is often EASY TO DO !, because he is stateless! You only need to look at the previous frame !!!!)

In other words, a gesture is a trigger when the decomposition / contraction of the VELOCITY of the fingers reaches a certain value, and not a multiple of the starting value.


More interesting footnote!

However, there is a subtle problem: whenever you do something like this (on any platform), you have to be careful to measure "on glass ... p>

IF you just make a distance (that is, my first decision is higher), of course, everything is canceled, and you can just say “if it doubles” (in pixels - dots - no matter what happens). BUT, if you perform speed as part of the calculation in any gesture, then it is somewhat surprising that you need to literally find the speed in meters per second in the real world , which seems strange at first! Of course, you cannot do it for sure (especially with android). The size of the glass depends a little, but you need to get closer to it. Here is a long post discussing this issue http://answers.unity3d.com/questions/292333/how-to-calculate-swipe-speed-on-ios.html In practice, you usually have to do a "screen width per second" pretty good. (But it can be very different on phones, large tablets, and these days are “superficial” types of things. On your screen, the iMac 0.1 screenwidthspersecond can be fast, but on the iPhone it's nothing, not a gesture.)


Final footnote! I just don’t know if Apple uses “distance a few” or “glass speed” in gesture recognition, and it’s also probably a subtle mix. I never read an article from them, commenting on it.


Another footnote! - if for any reason you want to find the "center" of the triangle (I mean the center of three fingers). This is an interesting problem for game programmers, because, after all, all three-dimensional grids are triangles.

Fortunately, it is trivial to find the center of three points, just add three vectors and divide by three! (Vaguely it even works in higher dimensions !!)

You can see endless posts on this issue ...

http://answers.unity3d.com/questions/445442/calculate-uv-at-center-of-triangle.html

http://answers.unity3d.com/questions/424950/mid-point-of-a-triangle.html

Perhaps if you were incredibly anal, you would like the “barycenter” to be more like a center of mass, just Google, if you want it.

+3
source

You need to create your own subclass of UIGestureRecognizer (call it DRThreeFingerPinchGestureRecognizer ) and execute:

 touchesBegan:withEvent: – touchesMoved:withEvent: – touchesEnded:withEvent: – touchesCancelled:withEvent: 

These methods are called when touches are accepted by the system and, possibly, before they are sent to the presentation (depending on how you configure the gesture recognizer). Each of these methods will give you a set of touches for which you can check the current location in your view and in the previous location. Since the pinch gesture is relatively simple, this information is enough to check if the user is working and not to run the test ( UIGestureRecognizerStateFailed ). If the state was not met with – touchesEnded:withEvent: you can recognize the gesture.

I say that pinch gestures are simple because you can easily track every touch and see how it moves compared to other touches and to yourself. If the corner threshold is passed and broken, you will not pass the test, otherwise you will allow it to continue. If the touches do not move at different angles to each other, you do not perform the test. You will have to play with which angles of the vectors are acceptable, because 120 degrees are not optimal for the three most common fingers (finger pointer + index + middle). You may just want to check that the vectors do not collide.

Be sure to read the UIGestureRecognizer documentation for an in-depth study of the various methods, as well as subclass notes.

+4
source

I think the corners of the track are leading you the wrong way. I think this is more likely a more flexible and intuitive gesture if you don't restrain it based on the angles between your fingers. This will be less error prone if you just handle it like a three-finger pinch, regardless of how the fingers move relative to each other. This is what I would do:

 if(presses != 3) { state = UIGestureRecognizerStateCancelled; return; } // After three fingers are detected, begin tracking the gesture. state = UIGestureRecognizerStateBegan; central_point_x = (point1.x + point2.x + point3.x) / 3; central_point_y = (point1.y + point2.y + point3.y) / 3; // Record the central point and the average finger distance from it. central_point = make_point(central_point_x, central_point_y); initial_pinch_amount = (distance_between(point1, central_point) + distance_between(point2, central_point) + distance_between(point3, central_point)) / 3; 

Then for each update for touches moved:

 if(presses != 3) { state = UIGestureRecognizerStateEnded; return; } // Get the new central point central_point_x = (point1.x + point2.x + point3.x) / 3; central_point_y = (point1.y + point2.y + point3.y) / 3; central_point = make_point(central_point_x, central_point_y); // Find the new average distance pinch_amount = (distance_between(point1, central_point) + distance_between(point2, central_point) + distance_between(point3, central_point)) / 3; // Determine the multiplicative factor between them. difference_factor = pinch_amount / initial_pinch_amount 

Then you can do whatever you want with factor_difference. If it is more than 1, then the pinch has moved away from the center. If it is less than one, it moves to the center. It will also give the user the ability to hold two fingers still and only move a third to complete your gesture. This will solve some of the accessibility issues that your users may encounter.

In addition, you can always track incremental changes in touch events, but they will not be evenly distributed over time, and I suspect that you will have more problems with this.

I also apologize for the pseudo code. If something is unclear, I can look at a real example.

+3
source

A simple subclass of UIGestureRecognizer. It calculates the relative triangular center of 3 points, and then calculates the average distance from this center, the angle is not important. Then you check the average distance in your gesture descriptor.

.h

 #import <UIKit/UIKit.h> #import <UIKit/UIGestureRecognizerSubclass.h> @interface UnPinchGestureRecognizer : UIGestureRecognizer @property CGFloat averageDistanceFromCenter; @end 

.m

 #import "UnPinchGestureRecognizer.h" @implementation UnPinchGestureRecognizer -(CGPoint)centerOf:(CGPoint)pnt1 pnt2:(CGPoint)pnt2 pnt3:(CGPoint)pnt3 { CGPoint center; center.x = (pnt1.x + pnt2.x + pnt3.x) / 3; center.y = (pnt1.y + pnt2.y + pnt3.y) / 3; return center; } -(CGFloat)averageDistanceFromCenter:(CGPoint)center pnt1:(CGPoint)pnt1 pnt2:(CGPoint)pnt2 pnt3:(CGPoint)pnt3 { CGFloat distance; distance = (sqrt(fabs(pnt1.x-center.x)*fabs(pnt1.x-center.x)+fabs(pnt1.y-center.y)*fabs(pnt1.y-center.y))+ sqrt(fabs(pnt2.x-center.x)*fabs(pnt2.x-center.x)+fabs(pnt2.y-center.y)*fabs(pnt2.y-center.y))+ sqrt(fabs(pnt3.x-center.x)*fabs(pnt3.x-center.x)+fabs(pnt3.y-center.y)*fabs(pnt3.y-center.y)))/3; return distance; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if ([touches count] == 3) { [super touchesBegan:touches withEvent:event]; NSArray *touchObjects = [touches allObjects]; CGPoint pnt1 = [[touchObjects objectAtIndex:0] locationInView:self.view]; CGPoint pnt2 = [[touchObjects objectAtIndex:1] locationInView:self.view]; CGPoint pnt3 = [[touchObjects objectAtIndex:2] locationInView:self.view]; CGPoint center = [self centerOf:pnt1 pnt2:pnt2 pnt3:pnt3]; self.averageDistanceFromCenter = [self averageDistanceFromCenter:center pnt1:pnt1 pnt2:pnt2 pnt3:pnt3]; self.state = UIGestureRecognizerStateBegan; } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { if ([touches count] == 3) { NSArray *touchObjects = [touches allObjects]; CGPoint pnt1 = [[touchObjects objectAtIndex:0] locationInView:self.view]; CGPoint pnt2 = [[touchObjects objectAtIndex:1] locationInView:self.view]; CGPoint pnt3 = [[touchObjects objectAtIndex:2] locationInView:self.view]; CGPoint center = [self centerOf:pnt1 pnt2:pnt2 pnt3:pnt3]; self.averageDistanceFromCenter = [self averageDistanceFromCenter:center pnt1:pnt1 pnt2:pnt2 pnt3:pnt3]; self.state = UIGestureRecognizerStateChanged; return; } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; self.state = UIGestureRecognizerStateEnded; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; self.state = UIGestureRecognizerStateFailed; } @end 

the implementation of the gesture, I have the maximum distance avg set, and then the minimum to the end, you can also check during the change:

 -(IBAction)handleUnPinch:(UnPinchGestureRecognizer *)sender { switch (sender.state) { case UIGestureRecognizerStateBegan: //If you want a maximum starting distance self.validPinch = (sender.averageDistanceFromCenter<75); break; case UIGestureRecognizerStateEnded: //Minimum distance from relative center if (self.validPinch && sender.averageDistanceFromCenter >=150) { NSLog(@"successful unpinch"); } break; default: break; } } 
+1
source

All Articles