iOS开发基础29-触摸事件及手势识别

在iOS应用中,用户的各种操作会产生多种事件。了解并掌握这些事件的处理方式对于构建高质量的iOS应用至关重要。本文将详细介绍iOS中的三大类型事件、响应者对象、触摸事件的处理、事件的产生与传递以及常见手势识别的处理方式。

一、iOS中的事件类型

iOS中的事件可以分为三大类型:

  1. 触摸事件:用户通过触摸屏幕产生的事件。
  2. 加速计事件:设备加速度的变化产生的事件。
  3. 远程控制事件:如控制音乐播放等产生的事件。

二、响应者对象(Responder Object)

在iOS中,不是所有对象都能处理事件,只有继承了UIResponder的对象才能接收和处理事件,这些对象被称为“响应者对象”。常见的UIResponder子类有:

  • UIApplication
  • UIViewController
  • UIView

这些类都能够接收并处理事件。

三、UIResponder事件处理方法

UIResponder内部提供了处理不同类型事件的方法:

触摸事件方法

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

加速计事件方法

  • - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
  • - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
  • - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;

远程控制事件方法

  • - (void)remoteControlReceivedWithEvent:(UIEvent *)event;

四、UIView的触摸事件处理

触摸事件方法

作为UIResponder的子类,UIView可以覆盖以下方法来处理触摸事件:

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

触摸事件的处理

以下是一个基本的示例:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"触摸开始");
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"触摸移动");
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"触摸结束");
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"触摸取消");
}

五、UITouch对象

当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象。每根手指对应一个UITouch对象。

UITouch属性

  • window:触摸产生时所处的窗口
  • view:触摸产生时所处的视图
  • tapCount:触摸的次数
  • timestamp:触摸事件的时间戳
  • phase:触摸事件的状态

UITouch方法

  • - (CGPoint)locationInView:(UIView *)view:返回触摸在指定视图中的位置
  • - (CGPoint)previousLocationInView:(UIView *)view:返回前一个触摸点在指定视图中的位置

六、UIEvent对象

每产生一个事件,就会创建一个UIEvent对象,记录事件产生的时刻和类型。

常见属性

  • type:事件类型
  • subtype:事件子类型
  • timestamp:事件时间戳

UIEvent的方法

  • - (NSSet<UITouch *> *)touchesForView:(UIView *)view:获取指定视图上的触摸对象

七、事件的产生和传递

事件的产生

触摸事件发生后,系统会将该事件加入到由UIApplication管理的事件队列中,UIApplication会从事件队列中取出事件,并分发给应用程序的主窗口(keyWindow),主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。

事件的传递

触摸事件的传递是从父控件传递到子控件,最终找到合适的视图处理事件。

以下是hitTest:withEvent:方法的伪代码示例:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    if ([self pointInside:point withEvent:event] == NO) return nil;
    
    for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
        CGPoint convertedPoint = [subview convertPoint:point fromView:self];
        UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
        if (hitTestView) {
            return hitTestView;
        }
    }
    return self;
}

该方法判断控件是否能接收事件以及触摸点是否在控件上,如果控件不能接收事件或者触摸点不在控件上,直接返回nil。否则,从后往前遍历子控件数组,直到找到最合适的视图。

八、响应者链条

响应者链条的示意图

响应者链条是由多个响应者对象连接起来的链条。它能够让一个事件被多个对象处理。

事件传递的完整过程

  1. 先将事件对象由上往下传递,找到最合适的视图控件来处理事件。
  2. 调用最合适控件的touches…方法。
  3. 如果调用了[super touches…],就会将事件顺着响应者链条向上传递。
  4. 继续调用上一个响应者的touches…方法。

判断上一个响应者

  • 如果当前视图是控制器的视图,那么控制器是上一个响应者。
  • 如果当前视图不是控制器的视图,那么父控件是上一个响应者。
  • 如果最顶级的视图也不能处理事件,则事件传递给window对象,再传递给UIApplication对象。

九、手势识别

iOS 3.2之后,苹果推出了手势识别功能,大大简化了触摸事件的处理。

常见手势识别器

  • UITapGestureRecognizer(点按)
  • UIPinchGestureRecognizer(捏合)
  • UIPanGestureRecognizer(拖拽)
  • UISwipeGestureRecognizer(轻扫)
  • UIRotationGestureRecognizer(旋转)
  • UILongPressGestureRecognizer(长按)

手势识别的使用步骤

UITapGestureRecognizer为例:

// 1. 创建手势识别器对象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];

// 2. 设置手势识别器的属性
tap.numberOfTapsRequired = 2; // 连续敲击两次
tap.numberOfTouchesRequired = 1; // 使用一根手指

// 3. 添加手势识别器到对应的视图
[self.view addGestureRecognizer:tap];

// 4. 实现手势识别方法
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
    NSLog(@"Tap gesture recognized");
}

其他手势识别器示例

长按手势识别(UILongPressGestureRecognizer

- (void)setUpLongPress {
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    [self.view addGestureRecognizer:longPress];
}

- (void)longPress:(UILongPressGestureRecognizer *)longPress {
    if (longPress.state == UIGestureRecognizerStateEnded) {
        NSLog(@"长按手势识别结束");
    }
}

轻扫手势识别(UISwipeGestureRecognizer

- (void)setUpSwipe {
    UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
    swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
    [self.view addGestureRecognizer:swipeRight];

    UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
    swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.view addGestureRecognizer:swipeLeft];
}

- (void)handleSwipe:(UISwipeGestureRecognizer *)swipe {
    if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
        NSLog(@"向右轻扫");
    } else if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
        NSLog(@"向左轻扫");
    }
}

旋转手势识别(UIRotationGestureRecognizer

- (void)setUpRotation {
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotation:)];
    [self.view addGestureRecognizer:rotation];
}

- (void)handleRotation:(UIRotationGestureRecognizer *)rotation {
    self.view.transform = CGAffineTransformRotate(self.view.transform, rotation.rotation);
    rotation.rotation = 0;
}

捏合手势识别(UIPinchGestureRecognizer

- (void)setUpPinch {
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
    [self.view addGestureRecognizer:pinch];
}

- (void)handlePinch:(UIPinchGestureRecognizer *)pinch {
    self.view.transform = CGAffineTransformScale(self.view.transform, pinch.scale, pinch.scale);
    pinch.scale = 1;
}

拖拽手势识别(UIPanGestureRecognizer

- (void)setUpPan {
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [self.view addGestureRecognizer:pan];
}

- (void)handlePan:(UIPanGestureRecognizer *)pan {
    CGPoint translation = [pan translationInView:self.view];
    self.view.transform = CGAffineTransformTranslate(self.view.transform, translation.x, translation.y);
    [pan setTranslation:CGPointZero inView:self.view];
}

多手势同时识别

可以通过实现UIGestureRecognizerDelegate协议方法来支持多手势同时识别:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

手势识别状态

typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
    UIGestureRecognizerStatePossible,
    UIGestureRecognizerStateBegan,
    UIGestureRecognizerStateChanged,
    UIGestureRecognizerStateEnded,
    UIGestureRecognizerStateCancelled,
    UIGestureRecognizerStateFailed,
    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
};
posted @ 2015-08-02 21:02  Mr.陳  阅读(3092)  评论(2编辑  收藏  举报