iOS tap touch and gesture code development

  • 2020-12-22 17:48:03
  • OfStack

1. Responder chain

Any class with UIResponder as its superclass is a responder. UIView and UIControl are subclasses of UIReponder, so all views and all controls are responders.

1. Initial responder
The event is passed first to the UIApplication object, then to the application's UIWindow, which selects an initial correspondent to handle the event. The initial responder selects the following options: 1. For touch events, UIWindow determines the view the user touched and then hands the event to the gesture recognizer that registers the view or to the gesture recognizer that registers the view at a higher level. As long as there is a recognizer that can handle the event, the search will stop. If not, the touched view is the initial counterpart, and the event is passed to it.

2. Events arising from the user shaking the device or from the remote control device will be passed to the first responder
If the initial responder does not handle the time, it passes the event to its superview, if it exists, or to the view controller, if that view is the view controller's view. If the view controller does not handle the event, it continues down the responder chain to the superview controller (if any exists).
If there is no view or controller that can handle the event in the entire view hierarchy, the event is passed to the window of the application. If the window cannot handle events and the application delegate is a subclass of UIResponder, the UIApplication object passes it to the application delegate. Finally, if the application delegate is not a subclass of UIResponder, or does not handle this event, the event will be discarded.

Four gesture notification methods


#pragma mark - Touch Event Methods
//  The user first 1 Called when the screen is touched a second time 
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

}

//  Called when some event (such as incoming call) causes the gesture to break 
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

}

//  Called when the user's finger leaves the screen 
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

}

//  Triggered when the user's finger moves 
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

}

2. Detect scan events

1. Manual detection


//
// ViewController.m
// Swipes
//
// Created by Jierism on 16/8/4.
// Copyright © 2016 years  Jierism. All rights reserved.
//

#import "ViewController.h"
//  Set detection range 
static CGFloat const kMinimmGestureLength = 25;
static CGFloat const kMaximmVariance = 5;

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (nonatomic) CGPoint gestureStartPoint;
@end

@implementation ViewController


- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  UITouch *touch = [touches anyObject];
  self.gestureStartPoint = [touch locationInView:self.view];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  UITouch *touch = [touches anyObject];
  CGPoint currentPosition = [touch locationInView:self.view];
  //  return 1 a float The absolute value of 
  CGFloat deltaX = fabsf(self.gestureStartPoint.x - currentPosition.x);
  CGFloat deltaY = fabsf(self.gestureStartPoint.y - currentPosition.y);
  
  //  After obtaining two deltas, judge the distance that the user has moved in both directions, and detect whether the user is in 1 It's moving far enough in one direction but at the other 1 Not enough movement in all directions to create a sweep 
  if (deltaX >= kMinimmGestureLength && deltaY <= kMaximmVariance) {
    self.label.text = @"Horizontal swipe detected";
    // 2s After erasing the text 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)),
            dispatch_get_main_queue(),
            ^{
      self.label.text = @"";
    });
  }else if (deltaY >= kMinimmGestureLength && deltaX <= kMaximmVariance){
    self.label.text = @"Vertical swipe detected";
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      self.label.text = @"";
    });
  }
}

@end

2. Recognizer detection


//
// ViewController.m
// Swipes
//
// Created by Jierism on 16/8/4.
// Copyright © 2016 years  Jierism. All rights reserved.
//

#import "ViewController.h"


@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (nonatomic) CGPoint gestureStartPoint;
@end

@implementation ViewController


- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  // Create two gesture recognizers 
  // 1、水平方向识别器
  UISwipeGestureRecognizer *horizontal = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(reportHorizontalSwipe:)];

  horizontal.direction = UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;
  [self.view addGestureRecognizer:horizontal];
  
  // 2 Vertical direction recognizer 
  UISwipeGestureRecognizer *vertical = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(reportVerticalSwipe:)];
  vertical.direction = UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown;
  [self.view addGestureRecognizer:vertical];
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

- (void)reportHorizontalSwipe:(UIGestureRecognizer *)recognizer
{

  self.label.text = @"Horizontal swipe detected";
  // 2s After erasing the text 
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)),
          dispatch_get_main_queue(),
          ^{
            self.label.text = @"";
          });
}

- (void)reportVerticalSwipe:(UIGestureRecognizer *)recognizer
{
  self.label.text = @"Vertical swipe detected";
  // 2s After erasing the text 
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)),
          dispatch_get_main_queue(),
          ^{
            self.label.text = @"";
          });
}

@end

3. Realize multi-finger swipe


//
// ViewController.m
// Swipes
//
// Created by Jierism on 16/8/4.
// Copyright © 2016 years  Jierism. All rights reserved.
//

#import "ViewController.h"



@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (nonatomic) CGPoint gestureStartPoint;
@end

@implementation ViewController


- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  
  for (NSUInteger touchCount = 1; touchCount <= 5; touchCount++) {
    // Create two gesture recognizers 
    // 1 Horizontal direction recognizer 
    UISwipeGestureRecognizer *horizontal = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(reportHorizontalSwipe:)];
    
    horizontal.direction = UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;
    [self.view addGestureRecognizer:horizontal];
    
    // 2 Vertical direction recognizer 
    UISwipeGestureRecognizer *vertical = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(reportVerticalSwipe:)];
    vertical.direction = UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown;
    [self.view addGestureRecognizer:vertical];
  }
  
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

- (NSString *)descriptionForTouchCount:(NSUInteger)touchCount
{
  switch (touchCount) {
    case 1:
      return @"Single";
    case 2:
      return @"Double";
    case 3:
      return @"Triple";
    case 4:
      return @"Quadruple";
    case 5:
      return @"Quintuple";
      
    default:
      return @"";
  }
}

- (void)reportHorizontalSwipe:(UIGestureRecognizer *)recognizer
{

  self.label.text = [NSString stringWithFormat:@"%@ Horizontal swipe detected",[self descriptionForTouchCount:[recognizer numberOfTouches]]];
  // 2s After erasing the text 
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)),
          dispatch_get_main_queue(),
          ^{
            self.label.text = @"";
          });
}

- (void)reportVerticalSwipe:(UIGestureRecognizer *)recognizer
{
  self.label.text = [NSString stringWithFormat:@"%@ Vertical swipe detected",[self descriptionForTouchCount:[recognizer numberOfTouches]]];
  // 2s After erasing the text 
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)),
          dispatch_get_main_queue(),
          ^{
            self.label.text = @"";
          });
}

@end

4. Test multiple light points


//
// ViewController.m
// TapTaps
//
// Created by Jierism on 16/8/4.
// Copyright © 2016 years  Jierism. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *singleLabel;
@property (weak, nonatomic) IBOutlet UILabel *doubleLabel;
@property (weak, nonatomic) IBOutlet UILabel *tripleLabel;
@property (weak, nonatomic) IBOutlet UILabel *quadrupleLabel;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  //  create 4 Three click gesture recognizers 
  UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap)];
  singleTap.numberOfTapsRequired = 1;
  singleTap.numberOfTouchesRequired = 1;
  //  Attach to view 
  [self.view addGestureRecognizer:singleTap];
  
  UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap)];
  doubleTap.numberOfTapsRequired = 2;
  doubleTap.numberOfTouchesRequired = 1;
  [self.view addGestureRecognizer:doubleTap];
  //  when doubleTap Run in response to "failure" singleTap
  [singleTap requireGestureRecognizerToFail:doubleTap];
  
  UITapGestureRecognizer *tripleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tripleTap)];
  tripleTap.numberOfTapsRequired = 3;
  tripleTap.numberOfTouchesRequired = 1;
  [self.view addGestureRecognizer:tripleTap];
  [doubleTap requireGestureRecognizerToFail:tripleTap];
  
  UITapGestureRecognizer *quadrupleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(quadrupleTap)];
  quadrupleTap.numberOfTapsRequired = 4;
  quadrupleTap.numberOfTouchesRequired = 1;
  [self.view addGestureRecognizer:quadrupleTap];
  [tripleTap requireGestureRecognizerToFail:quadrupleTap];
}

- (void)singleTap
{
  self.singleLabel.text = @"Single Tap Detected";
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    self.singleLabel.text = @"";
  });
}

- (void)doubleTap
{
  self.doubleLabel.text = @"Double Tap Detected";
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    self.doubleLabel.text = @"";
  });
}

- (void)tripleTap
{
  self.tripleLabel.text = @"Triple Tap Detected";
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    self.tripleLabel.text = @"";
  });
}

- (void)quadrupleTap
{
  self.quadrupleLabel.text = @"Quadruple Tap Detected";
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    self.quadrupleLabel.text = @"";
  });
}


@end

5. Detect kneading and rotation


#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<UIGestureRecognizerDelegate>


@end


//
// ViewController.m
// PinchMe
//
// Created by Jierism on 16/8/4.
// Copyright © 2016 years  Jierism. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property (strong,nonatomic) UIImageView *imageView;

@end

@implementation ViewController

//  Current scale, previous scale 
CGFloat scale,previousScale;
//  Current rotation Angle, previous rotation Angle 
CGFloat rotation,previousRotation;

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  previousScale = 1;
  
  UIImage *image = [UIImage imageNamed:@"yosemite-meadows"];
  self.imageView = [[UIImageView alloc] initWithImage:image];
  //  Enable interactive features for images 
  self.imageView.userInteractionEnabled = YES;
  self.imageView.center = self.view.center;
  [self.view addSubview:self.imageView];
  
  //  Construct knead gesture recognizer 
  UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(doPinch:)];
  pinchGesture.delegate = self;
  [self.imageView addGestureRecognizer:pinchGesture];
  
  //  Set up a rotation gesture recognizer 
  UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(doRorate:)];
  rotationGesture.delegate = self;
  [self.imageView addGestureRecognizer:rotationGesture];
}


- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
  //  Allows the knead and spin gestures to work simultaneously. Otherwise, the initial gesture recognizer will mask the other 1 a 
  return YES;
}

//  The image is transformed according to the scale and rotation Angle obtained in the gesture recognizer 
- (void)transformImageView
{
  CGAffineTransform t = CGAffineTransformMakeScale(scale * previousScale, scale * previousScale);
  t = CGAffineTransformRotate(t, rotation + previousRotation);
  self.imageView.transform = t;
}

- (void)doPinch:(UIPinchGestureRecognizer *)gesture
{
  scale = gesture.scale;
  [self transformImageView];
  if (gesture.state == UIGestureRecognizerStateEnded) {
    previousScale = scale * previousScale;
    scale = 1;
  }
}


- (void)doRorate:(UIRotationGestureRecognizer *)gesture
{
  rotation = gesture.rotation;
  [self transformImageView];
  if (gesture.state == UIGestureRecognizerStateEnded) {
    previousRotation = rotation + previousRotation;
    rotation = 0;
  }
}


@end

6. Custom gestures


//
// CheckMarkRecognizer.m
// CheckPlease
//
// Created by Jierism on 16/8/4.
// Copyright © 2016 years  Jierism. All rights reserved.
//

#import "CheckMarkRecognizer.h"
#import "CGPointUtils.h"
#import <UIKit/UIGestureRecognizerSubclass.h> // 1 An important purpose is to make gesture recognizers state Property is writable, and the subclass will use this mechanism to assert that the gesture we observe has completed successfully 

//  Set detection range 
static CGFloat const kMinimunCheckMarkAngle = 80;
static CGFloat const kMaximumCheckMarkAngle = 100;
static CGFloat const kMinimumCheckMarkLength = 10;

@implementation CheckMarkRecognizer{
  //  The first two instance variables provide the preceding line segment 
  CGPoint lastPreviousPoint;
  CGPoint lastCurrentPoint;
  //  The length of the line segment 
  CGFloat lineLengthSoFar;
}


//  with lastPreviousPoint and lastCurrentPoint Of the first 1 A line segment, followed by a control 2 The line segment forms an Angle to complete the gesture 
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  [super touchesBegan:touches withEvent:event];
  UITouch *touch = [touches anyObject];
  CGPoint point = [touch locationInView:self.view];
  lastPreviousPoint = point;
  lastCurrentPoint = point;
  lineLengthSoFar = 0.0;
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  [super touchesMoved:touches withEvent:event];
  UITouch *touch = [touches anyObject];
  CGPoint previousPoint = [touch previousLocationInView:self.view];
  CGPoint currentPoint = [touch locationInView:self.view];
  CGFloat angle = angleBetweenLines(lastPreviousPoint, lastCurrentPoint, previousPoint, currentPoint);
  if (angle >= kMinimunCheckMarkAngle && angle <= kMaximumCheckMarkAngle && lineLengthSoFar > kMinimumCheckMarkLength) {
    self.state = UIGestureRecognizerStateRecognized;
  }
  lineLengthSoFar += distanceBetweenPoints(previousPoint, currentPoint);
  lastPreviousPoint = previousPoint;
  lastCurrentPoint = currentPoint;
}

@end

//
// ViewController.m
// CheckPlease
//
// Created by Jierism on 16/8/4.
// Copyright © 2016 years  Jierism. All rights reserved.
//

#import "ViewController.h"
#import "CheckMarkRecognizer.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIImageView *imageView;


@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  CheckMarkRecognizer *check = [[CheckMarkRecognizer alloc] initWithTarget:self action:@selector(doCheck:)];
  [self.view addGestureRecognizer:check];
  self.imageView.hidden = YES;
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

- (void)doCheck:(CheckMarkRecognizer *)check
{
  self.imageView.hidden = NO;
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    self.imageView.hidden = YES;
  });
}


@end


Related articles: