Although there is already an example implementation of a custom checkmark gesture recognizer within the iOS Developer Library (I found it too late), I would like to provide a full example over here again. The objective is very simple: recognize a checkmark gesture within your iOS app on a given view to trigger some kind of event. All we need for that is just a custom gesture recognizer implementation which is able to recognize a checkmark. Further we will define how the gesture should look like so it is a valid checkmark. I used following simple constraints for that:

  • The checkmark has a given start point.
  • The checkmark has two strokes.
  • Each x position of a point within the first stroke is greater than the x position of the previous point (= stroke goes to the right).
  • Each y position of a point within the first stroke is greater than the y position of the previous point (= stroke goes down).
  • If a y position of a point is less than the y position of the previous point then we mark this point as the turn point and we will observe the second stroke (although I am talking about two strokes, they aren’t two independent touches and moves on the device rather than it should be seen as a single move).
  • Each x position of a point within the second stroke is greater than the x position of the previous point (= stroke goes to the right).
  • Each y position of a point within the first stroke is less than the y position of the previous point (= stroke goes up).
  • The second stroke has to be at least 2-times in length as the first stroke.

No we just need to realize these rules within a gesture recognizer implementation. I created a new subclass of UIGestureRecognizer (IMPORTANT: you have to import <UIKit/UIGestureRecognizerSubclass.h> instead of <UIKit/UIGestureRecognizer.h> so you can access and set the state of the gesture recognizer) and named it CheckmarkGestureRecognizer. Within this class it is necessary to override following methods:

- (void)reset;  - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;  - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;  - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;  - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

Also I added following class members and a custom enumeration:

typedef enum {      CheckmarkFirstStrokeDown,      CheckmarkSecondStrokeUp  } CheckmarkGestureState;
CheckmarkGestureState _checkmarkState;  CGPoint _turnPoint;  CGPoint _startPoint;

All gesture recognizers within the iOS SDK are based on an internal state of the type UIGestureRecognizerState. This state tells the gesture recognizer if it is trying to recognize a continues or discrete gesture and if the recognition was successful or did fail. Initially the state is set to UIGestureRecognizerStatePossible and if you try to recognize a discrete gesture (a gesture within a single touch/move) it shouldn’t change until the gesture is finished. As soon as the implementation did recognize the gesture the state will be set to UIGestureRecognizerStateRecognized and a corresponding delegate method will be called automatically. The source code of the mentioned methods above is given over here:

// Override the reset method. This methods gets  // called whenever the state changes to  // UIGestureRecognizerStateFailed.  - (void)reset  {      [super reset];      [self resetCheckmark];  }     // Touch began on view so save the start point  - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event  {      [super touchesBegan:touches withEvent:event];         if ([touches count] != 1) {          self.state = UIGestureRecognizerStateFailed;          return;      }      else      {          _startPoint = [[touches anyObject] locationInView:self.view];      }  }     // Track the move of the current gesture  - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event  {      [super touchesMoved:touches withEvent:event];         if (self.state == UIGestureRecognizerStateFailed)          return;         // Get the current and previous touch locations      CGPoint newPoint    = [[touches anyObject] locationInView:self.view];      CGPoint prevPoint   = [[touches anyObject] previousLocationInView:self.view];         // If we track the first stroke      if (_checkmarkState == CheckmarkFirstStrokeDown)      {          // While we move to the right and downwards          if (newPoint.x &gt;= prevPoint.x &amp;&amp; newPoint.y &gt;= prevPoint.y)          {              // Keep the possible turn point of the stroke              _turnPoint = newPoint;          }          // Else if we change direction to the right and upwards          else if (newPoint.x &gt;= prevPoint.x &amp;&amp; newPoint.y &lt;= prevPoint.y)          {              // Set the state to observe the second stroke              _checkmarkState = CheckmarkSecondStrokeUp;          }          else          {              // In any other case the gesture will fail              self.state = UIGestureRecognizerStateFailed;          }      }// Else we track the second stroke      else if(_checkmarkState == CheckmarkSecondStrokeUp)      {          if(newPoint.x &lt; prevPoint.x)             self.state = UIGestureRecognizerStateFailed;     } } // Track the end of the gesture - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {     [super touchesEnded:touches withEvent:event];          if ((self.state == UIGestureRecognizerStatePossible) &amp;&amp; _checkmarkState == CheckmarkSecondStrokeUp)     {         // Get length of both strokes         CGPoint endPoint = [[touches anyObject] locationInView:self.view];         float lengthFirstStroke     = _turnPoint.y - _startPoint.y;         float lengthSecondStroke    = _turnPoint.y - endPoint.y;                  // Checkmark gesture is valid if the second stroke has at         // least 2-times the length of the first stroke         if(lengthSecondStroke &gt; lengthFirstStroke * 2.0)          {              // Set state to UIGestureRecognizerStateRecognized and              // the gesture recognizer will call the defined action              self.state = UIGestureRecognizerStateRecognized;          }          else          {              self.state = UIGestureRecognizerStateFailed;          }      }  }     // The gesture will fail if touche was cancelled  - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event  {      [super touchesCancelled:touches withEvent:event];      self.state = UIGestureRecognizerStateFailed;  }

That’s all! Create a new instance of CheckmarkGestureRecognizer, set the target and action method and add it to a view. I did this for a example iPad application (you can check out the code at GitHub) and here you can watch the result.

創作者 shadow 的頭像


shadow 發表在 痞客邦 留言(0) 人氣()