zoukankan      html  css  js  c++  java
  • 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;
        }
      }
    }
  • 相关阅读:
    在Java和.Net中的MD5的一致性
    为Asp.net 网站新增发送手机短信功能
    ASP.NET如何防止页面重复提交
    转:Ajax调用Webservice和后台方法
    Ext 常用方法之一
    C#编程实战之类功能缺失
    Silverlight常用控件最佳实践之1.自定义TabControl禁用状态
    Blend4精选案例图解教程(五):可视数据管理
    DEDE织梦自定表单提交后自动发送邮件并到站长邮箱
    php常用数组相关处理函数(1)
  • 原文地址:https://www.cnblogs.com/wangxiaorui/p/5028956.html
Copyright © 2011-2022 走看看