Touches and Events

Touches can be as simple as a tap and release or as complex as gestures with many fingers. iOS has several ways to handle touches, and they come down to four techniques, from simplest to most complex and flexible:

1) Don't do anything. Classes like UIScrollview and UINavigationController do all their own touch handling. You get the actions without coding.

2) For UIControls the touch detection is built in, but you must handle requests from the control. For buttons and sliders, use target-action methods. You tell the control to send a message to an object when a certain behavior occurs. You define the message and specify the object. You can choose different messages and objects for different behaviors, including,

   UIControlEventTouchDown
   UIControlEventTouchDownRepeat
   UIControlEventTouchDragInside
   UIControlEventTouchDragOutside
   UIControlEventTouchDragEnter
   UIControlEventTouchDragExit
   UIControlEventTouchUpInside
   UIControlEventTouchUpOutside
   UIControlEventTouchCancel
   UIControlEventValueChanged

and there are more. They are defined in the UIControl class. See the Control Demo sample application. For a more elaborate example, see Apple's UICatalog sample code.

Pickers require a delegate object and work somwhat like a tableView.

3) Use UIControl touch methods. They allow you to detect a touch and track it over time. You can decide what to do for swipes and flicks (Apple makes a distinction - a flick is faster than a swipe) and other gestures. These are also defined in UIControl.

  - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
  - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
  - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
  - (void)cancelTrackingWithEvent:(UIEvent *)event;

3+) Apple added gesture recognizers to iOS 3.2 and later. These are special classes that are attached to a UIView. They contain code to intercept touches and recognize special gestures like two-finger pinches and rotates. These are tedious to code properly, so gesture recognizers are a great time saver.

4) Use UIResponder methods. These give you full control of multiple touches. (Remember to set multipleTouchEnabled to YES in the UIView). These are defined in the UIResponder class.

  - (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;

Here is a class that handles touches and uses gesture recognizers,

@interface RotationControl :UIControl <UIGestureRecognizerDelegate>
 {
  CGPoint penultimateLocation;
  CGPoint lastLocation;
  CFAbsoluteTime penultimateTime, antepenultimateTime, beginTime;
  CFAbsoluteTime touchTime; 
  CGFloat netRotation;
  CGFloat lastRotation;
  CGFloat netPinch;
  CGFloat lastPinch;
 }
@end
@implementation RotationControl
- (void) handleSpin:(UIRotationGestureRecognizer *)sender
   {
    GLSpriteAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    if (sender.state == UIGestureRecognizerStateBegan)
    {
     netRotation = 0;
     lastRotation = 0;
    }
    else
    if (sender.state == UIGestureRecognizerStateChanged)
    {
     netRotation = sender.rotation;  //in radians
     [appDelegate.glView postSpin: -(netRotation - lastRotation) * degreesPerRadian];
     lastRotation = netRotation;
    }
   }
- (void) handlePinch:(UIPinchGestureRecognizer *)sender
   {
    GLSpriteAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    if (sender.state == UIGestureRecognizerStateBegan)
    {
     netPinch = 1.0;
     lastPinch = 1.0;
    }
    else
    if (sender.state == UIGestureRecognizerStateChanged)
    {
     netPinch = sender.scale;
     [appDelegate.glView doPinch: lastPinch/netPinch];
     lastPinch = netPinch;
    }
   }
- (void) handleSwipe:(UISwipeGestureRecognizer *)sender
   {
    GLSpriteAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    if (sender.state == UIGestureRecognizerStateBegan)
    {
     lastLocation = [sender locationInView:self];
    }
    else
    if (sender.state == UIGestureRecognizerStateChanged)
    {
     CGPoint thisLocation = [sender locationInView:self];
     CGPoint deltaLocation;
     deltaLocation.x = thisLocation.x - lastLocation.x;
     deltaLocation.y = thisLocation.y - lastLocation.y;
     lastLocation = thisLocation;
     [appDelegate.glView doSwipe: deltaLocation];
    }
   }
//GestureRecognizer delegate method
   - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
   {
    return YES;
   }
- (id)initWithFrame:(CGRect)frame {
   if (self = [super initWithFrame:frame]) {
    UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc]
    initWithTarget:self action:@selector(handleSpin:)];
    [self addGestureRecognizer:rotationGesture];
    [rotationGesture setDelegate:self];
    [rotationGesture release];
   
    UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc]
    initWithTarget:self action:@selector(handlePinch:)];
    [self addGestureRecognizer:pinchGesture];
    [pinchGesture setDelegate:self];
    [pinchGesture release];
   
    UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc]
    initWithTarget:self action:@selector(handleSwipe:)];
    [self addGestureRecognizer:swipeGesture];
    [swipeGesture setDelegate:self];
    [swipeGesture setNumberOfTouchesRequired:2];
    [swipeGesture release];
   }
   return self;
   }
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
   {
    lastLocation = [touch locationInView:self];
    penultimateLocation = lastLocation;
   
    touchTime = CFAbsoluteTimeGetCurrent ();
    penultimateTime = touchTime;
    antepenultimateTime = touchTime;
    beginTime = touchTime;
    GLSpriteAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    [appDelegate.glView postRotation: 0: 0];
    return YES;//continue tracking
   }
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
   { 
    CGPoint location = [touch locationInView:self];
    float deltaX = lastLocation.x - location.x;
    float deltaY = lastLocation.y - location.y;
    penultimateLocation = lastLocation;
    lastLocation = location;
   
    antepenultimateTime = penultimateTime;
    penultimateTime = touchTime;
    touchTime = CFAbsoluteTimeGetCurrent ();
   
    GLSpriteAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    [appDelegate.glView postRotation: -deltaX*controlRate: -deltaY*controlRate];
    return YES;
   }
 
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
   {
    GLSpriteAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
   
    if([touch tapCount] >= 2)
    {
     [appDelegate.buttonController fadeView:appDelegate.buttonController.countLabel];
    }
    CGPoint location = [touch locationInView:self];
    float deltaX = penultimateLocation.x - location.x;
    float deltaY = penultimateLocation.y - location.y;
    lastLocation = location;
    antepenultimateTime = penultimateTime;
    penultimateTime = touchTime;
    touchTime = CFAbsoluteTimeGetCurrent ();
    CFAbsoluteTime deltaT = touchTime - antepenultimateTime;
    if (deltaT > 0.5)
    {
     deltaX = 0;
     deltaY = 0;
    }
    [appDelegate.glView postRotation: -deltaX*controlRate: -deltaY*controlRate];
   }
 
@end  //RotationControl

The page was last updated Wednesday, November 30, 2011 4:57 PM