ios用户事件响应和手势基础

iOS学习笔记— UIView用户事件响应 - KnightMareFrame

UIView除了负责展示内容给用户外还负责响应用户事件。本章主要介绍UIView用户交互相关的属性和方法。

1、交互相关的属性

userInteractionEnabled  默认是YES ,如果设置为NO则不响应用户事件,并且把当前控件从事件队列中删除。也就是说设置了userInterfaceEnabled属性的视图会打断响应者链导致该view的subview都无法响应事件。

multipleTouchEnabled   默认是NO,如果设置为YES则支持多点触碰。

exclusiveTouch  默认是NO,如果设置为YES则当前UIView会独占整个Touch事件。具体来说就是如果UIView设置了exclusiveTouch属性为YES则当这个UIView成为第一响应者时,在手指离开屏幕前其他view不会响应任何touch事件。

作用举例:UITableView的每个cell都需要使用exclusive,否则同时点击多个cell会触发每个视图的事件响应。手势识别会忽略此属性。

2、触摸响应

了解UIView的触碰响应之前,首先了解在iOS中触碰事件是什么,事件在视图模型中是如何传递的,视图在接收到一个事件是如何响应的。下面介绍触碰事件类UITouch和响应者链来解释事件的工作原理。

在iOS中UITouch类代表触碰事件。当用户触摸屏幕后就会产生相应的事件,所有相关的UITouch对象都被包装在事件中,被程序交由特定的对象处理。UITouch对象包括触碰的详细信息。

UITouch 含有5个属性:

window :触碰产生时所处的窗口,由于窗口可能发生变化,当前所在的窗口不一定是最开始的窗口。

view :触碰产生时所处的视图。由于视图可能发生变化,当前视图也不一定是最初的视图。

tapCount :短时间内轻击(tap)屏幕的次数,可根据tapCount判断单击、双击或更多的轻击。

timestamp :时间戳记录了触碰事件产生或变化时的时间。单位是秒。

phase :触碰事件在屏幕上有一个周期,即触碰开始、触碰点移动、触碰结束,中途取消。通过phase可以查看当前触碰事件在一个周期中所处的状态。UITouchPhase枚举:

UITouchPhaseBegan

UITouchPhaseMoved

UITouchPhaseStationary

UITouchPhaseEnded

UITouchPhaseCancelled

当手指触碰到屏幕,无论是单点还是多点触碰,事件都会开始,直到用户所有的手指都离开屏幕。期间所有的UITouch对象都被封装在UIEvent事件对象中,由程序分发给处理者。事件记录了这个周期中所有触碰对象状态的变化。

只要屏幕被触摸,系统会将诺干个触碰信息封装到UIEvent对象中发送给程序,由管理程序UIApplication对象将事件分发。

响应者对象 就是可以响应事件并对事件作出处理的对象。在iOS中UIResponder类定义了响应者对象的所有方法。UIApplication、UIWindow、UIViewController、UIView以及UIKit中继承自UIView的控件都间接或直接继承自UIResponder类,这些类都可以当做响应者。

响应者链 表示一系列响应者对象组成的事件传递的链条。当确定了第一响应者后,事件交由第一响应者处理,如果第一响应者不处理事件沿着响应者链传递,交给下一个响应者。一般来说,第一响应者是UIView对象或者UIView的子类对象,当其被触摸后事件交由它处理,如果它不处理,事件就会交给它的UIViewController处理(如果存在),然后是它的superview父视图对象,以此类推,直到顶层视图。如果顶层视图不处理则交给UIWindow对象处理,再到UIApplication对象(如果UIApplication继承自UIResponder)。如果整个响应者链都不响应这个事件则该事件被丢弃。

UIView类继承了UIResponder类,要对事件作出处理还需要重写UIResponder类中定义的事件处理函数。根据不同的触碰状态,程序会调用相应的处理函数,这些函数包括:

-(void) touchesBegan:(NSSet *)touches withEvents:(UIEvent *)event;

-(void) touchesMoved:(NSSet *)touches withEvents:(UIEvent *)event;

-(void) touchesEnded:(NSSet *)touches withEvents:(UIEvent *)event;

-(void) touchesCancelled:(NSSet *)touches withEvents:(UIEvent *)event;

这几个方法被调用时,对应了UITouch类中的phase属性的4个枚举值。当触碰被取消,如触碰过程中被来电打断,会调用touchesCancelled:touches:方法。

这些方法在开发中并不需要全部实现,可以根据需要重写特定的方法。这4个方法都有两个相同的参数:NSSet类型的touches和UIEvent类型的event。Touches表示触碰产生的所有的UITouch对象,event表示事件。因为UIEvent包含了整个触碰过程中所有的触碰对象,所以可以调用allTouches 方法获取该事件内所有触碰对象,也可以调用touchesForView;或者touchesForWindows;取出特定视图或者窗口上的触碰对象。在这几个事件中,都可以拿到触碰对象,然后根据其位置、状态、时间属性做逻辑处理。

轻击操作很容易引起歧义,比如用户点击了一次之后,并不知道用户是想单击还是只是双击的一部分,或者点了两次之后并不知道用户是想双击还是继续点击。可以使用延迟调用函数解决这个问题。

  -(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  {

    UITouch *touch = [touches anyObject];
            if (touch.tapCount == 1)
           {
                   [self performSelector:@selector(setBackground:) withObject:[UIColor blueColor] afterDelay:2];

            }
            else if(touch.tapCount == 2)
            {
                   [self cancelPreviousPerformRequestsWIthTarget:self              selector:@selector(setBackground:) object:[UIColor blueColor]];
                   self.view.backgroundColor = [UIColor redColor];
             }
    }

除了触碰事件外UIResponder还提供了运动事件的支持。

运动事件的方法:

-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event  摇动事件开始

-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event  摇动事件结束

-(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event 摇动事件被中断

远程事件:

-(void)remoteControlReceivedWithEvent:  音乐后台播放控制的时候会用到

第一响应者的相关函数:

- (BOOL)canBecomeFirstResponder     默认返回NO

- (BOOL)becomeFirstResponder

- (BOOL)canResignFirstResponder     默认返回YES

- (BOOL)resignFirstResponder;

- (BOOL)isFirstResponder

可以通过becomeFirstResponder方法注册成为第一响应者,通过resignFirstResponder方法不成为第一响应者。比如通过这两个方法操作UITextField来控制键盘的现隐藏。

3、手势

UIView关于手势的方法:

-(void) addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer  增加一个手势。

-(void) removeGestureRecognizer:(UIGestureRecognizer *)getureRecognizer 删除一个手势。

-(BOOL) gestureRecognizerShouldBegan:(UIGestureRecognizer *)gestureRecognizer  询问是否开始执行该手势,默认返回YES。

手势相比触碰事件的好处是可以直接使用已经定义好的手势,开发者不用自己计算手指移动轨迹。

UIGestureRecognizer 是一个手势基类,提供了简单的手势实现方式。衍生类如下:

UITabGestureRecognizer         轻击手势

UIPinchGestureRecognizer       捏合手势

UIRotationGestureRecognizer    旋转手势

UISwipeGestureRecognizer  轻扫手势

UIPanGestureRecognizer 拖拽手势

UILongPressGestrueRecognizer 长按手势

UIGestureRecognizer主要方法:

-(id) initWithTarget:action: 初始化方法

-(void)addTarget:action:    

-(void)removeTarget:action: 

主要属性:

UIGestureRecognizerState state  手势识别当前状态

有以下几种情况:

UIGestureRecognizerStatePossibel ,  未识别状态

UIGestureRecognizerStateBegan ,     手势开始

UIGestureRecognizerStateChanged ,  手势改变

UIGestureRecognizerStateEnded , 手势结束

UIGestureRecognizerStateFailured 手势失败,被其他事件中断。

UITabGestureRecognizer   轻击手势任意手指任意次数的点击

属性:

numberOfTapsRequired  点击次数

numberOfTouchesRequired  手指个数  

UIPinchGestureRecognizer   捏合或者扩张手势

属性:

scale :初始值为1,两手指距离减少则scale不断变小;两个手指重合则变为0;

velocity :初始值为0,手指移动的相对速度,两手指距离减少为负数,速度越快数值越少;两手指距离变大为整数,速度越快数值越大。

UIRotationGestureRecognizer  旋转手势

属性:

rotation :初始值为0,两手指的旋转弧度,顺时针旋转为正数,逆时针旋转为负数。

velocity :初始值为0手指一动的相对速度,顺时针为正数越快值越大;逆时针为负越快越小。

UISwipGestureRecognizer  轻扫手势,一个手势只能指定一个方向,如果需要指定多个方向需要多个手势

属性:

numberOfTouchesRequired : 手指个数

direction :手势方向,如UISwipeGestureRecognizerDirectionRight 向右

UIPanGestureRecognizer :  拖拽手势,相比轻扫手势,手指与屏幕的交互时间更长。

属性:

mininumNumberOfTouches :默认值为1,最少手指数量

maxnumNumberOfTouches :最大手指数量

UILongPressGestrueRecognizer : 长按手势。

属性:

numberOfTapsRequired :默认值为0,轻击的次数。

numberOfTouchesRequired :默认值是1,手指数量。

mininumPressDuration :默认值为0.5,单位是秒。

allowableMovement :默认值为10,单位是像素。

UIGestureRecognizer 的子类分别有很多手势,通过 不用的手势可以执行不同的操作,下面来介绍下他们的基本使用方法所有手势配置基本相同,只是针对不同的手势里面有部分属性可以设置,比如说tap点进去看他有两个参数可以设置一个是点击次数,和点击手指数可设置。如果不知道这个手势能配置说明参数,那么点击进入相应的.h 文件查看

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(processGestureRecongnizer:)];//点击手势
    [tapGesture setNumberOfTouchesRequired:1];
    [tapGesture setNumberOfTapsRequired:1];
    [_view addGestureRecognizer:tapGesture];
    [tapGesture release];
- (void)processGestureRecongnizer:(UIGestureRecognizer *)gesture
{
    if ([gesture isKindOfClass:[UITapGestureRecognizer class]]) {
        [self positionAnimation];
    }
}
#pragma mark -- InitUserInterface
- (void)initUserInterface
{
  _view = [[UIView alloc]init];
  [_view setBounds:CGRectMake(0, 0, 200, 200)];
  [_view setCenter:CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))];
  [_view setBackgroundColor:[UIColor grayColor]];
  [self.view addSubview:_view];
  //两手指拨动手势
  UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(processGestureRecognizer:)];
  [pinch setDelegate:self];//设置代理
  [_view addGestureRecognizer:pinch]; //对view添加这个手势
  [pinch release];
  //旋转手势
  UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(processGestureRecognizer:)];
  [rotation setDelegate:self];
  [_view addGestureRecognizer:rotation];
  [rotation release];
  //长按手势
  UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(processGestureRecognizer:)];
  longPress.minimumPressDuration = 2;
  [_view addGestureRecognizer:longPress];
  [longPress release];
  //滑动手势--左
UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(processGestureRecognizer:)];
  [leftSwipe setDirection:UISwipeGestureRecognizerDirectionLeft];//配置滑动方向
  [self.view addGestureRecognizer:leftSwipe];
  [leftSwipe release];
  //滑动手势--右
  UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(processGestureRecognizer:)];
  [rightSwipe setDirection:UISwipeGestureRecognizerDirectionRight];
  [self.view addGestureRecognizer:rightSwipe];
  [rightSwipe release];
  //拖移手势
  UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(processGestureRecognizer:)];
  [pan setDelegate:self];
  [_view addGestureRecognizer:pan];
  [pan release];
  

}
#pragma mark -- GestureRrecognizer methods
//代理方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
  return NO;
}
// 手势action 让对应的手势执行相应地操作
- (void)processGestureRecognizer:(UIGestureRecognizer *)gesture
{
  //判断手势类型
  if ([gesture isKindOfClass:[UIPinchGestureRecognizer class]])
  {
    UIPinchGestureRecognizer *pinch = (UIPinchGestureRecognizer *)gesture;
    static float lastScale;//静态变量记录上次大小
    if (pinch.state == UIGestureRecognizerStateBegan) {
      lastScale = pinch.scale;// 手势开始把初始scale赋给静态变量方便更新
    }else if (pinch.state == UIGestureRecognizerStateChanged){
      [_view setTransform:CGAffineTransformScale(_view.transform, 1+(pinch.scale - lastScale), 1+(pinch.scale - lastScale))];//让View进行动态的放大或缩小
      lastScale = pinch.scale;// 更新scale的值 --(这样做让view保持变化后的状态而不会是初始状态)
      //此方法不需要更新lastScale的值  都是从原型开始
//			[_view setTransform:CGAffineTransformMakeScale(1+(pinch.scale - lastScale), 1+(pinch.scale - lastScale))];
    }
  }else if ([gesture isKindOfClass:[UIRotationGestureRecognizer class]])
  {
    UIRotationGestureRecognizer *rotation = (UIRotationGestureRecognizer *)gesture;
    static float lastRotation;
    if (rotation.state == UIGestureRecognizerStateBegan) {
      lastRotation = rotation.rotation;
    }else if (rotation.state == UIGestureRecognizerStateChanged){
      [_view setTransform:CGAffineTransformRotate(_view.transform, rotation.rotation - lastRotation)];
      lastRotation = rotation.rotation;
    }
  }
  else if ([gesture isKindOfClass:[UILongPressGestureRecognizer class]])
  {
    UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)gesture;
    if (longPress.state == UIGestureRecognizerStateBegan) {
      UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"温馨提示" message:@"Long Press Begin" delegate:self cancelButtonTitle:@"确定" otherButtonTitles: nil];
      [alert show];
      [alert release];
    }
  }
  else if ([gesture isKindOfClass:[UISwipeGestureRecognizer class]])
  {
    UISwipeGestureRecognizer *swipe = (UISwipeGestureRecognizer *)gesture;
    if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
      [UIView animateWithDuration:1 animations:^{
        [_view setCenter:CGPointMake(CGRectGetMinX(self.view.bounds), CGRectGetMidY(self.view.bounds))];
      }];
    }else{
      [UIView animateWithDuration:1 animations:^{
        [_view setCenter:CGPointMake(CGRectGetMaxX(self.view.bounds), CGRectGetMidY(self.view.bounds))];
      }];
    }
    
  }
  else if ([gesture isKindOfClass:[UIPanGestureRecognizer class]])
  {
    UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gesture;
    static CGPoint lastLocation;
    if (pan.state == UIGestureRecognizerStateBegan) {
      lastLocation = _view.center;
    }else if (pan.state == UIGestureRecognizerStateChanged)
    {
      CGPoint translationPoint = [pan translationInView:self.view];
      _view.center = CGPointMake(translationPoint.x + lastLocation.x, translationPoint.y+ lastLocation.y);
    }else if (pan.state == UIGestureRecognizerStateEnded)
    {
      lastLocation = CGPointZero;
    }
  }
}
原文地址:https://www.cnblogs.com/wangxiaorui/p/5028956.html