• UIView用户事件响应


    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;取出 特定视图或者窗口上的触碰对象。在这几个事件中,都可以拿到触碰对象,然后根据其位置、状态、时间属性做逻辑处理。

     

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

     

    1. -(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event  
    2. {  
    3.   
    4.   UITouch *touch = [touches anyObject];  
    5.           if (touch.tapCount == 1)  
    6.          {  
    7.                  [self performSelector:@selector(setBackground:) withObject:[UIColor blueColor] afterDelay:2];  
    8.   
    9.           }  
    10.           else if(touch.tapCount == 2)  
    11.           {  
    12.                  [self cancelPreviousPerformRequestsWIthTarget:self              selector:@selector(setBackground:) object:[UIColor blueColor]];  
    13.                  self.view.backgroundColor = [UIColor redColor];  
    14.            }  
    15.   }      

    除了触碰事件外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,单位是像素。

     

     

    借用一个项目来说明:   http://blog.csdn.net/chang6520/article/details/7924313

    首先新建一个基于Sigle view Application的项目,名为GestureTest;我的项目结构如下:

    往viewController.xib文件里拖动一个imageView,并使覆盖整个屏幕,改动属性为:

     

    viewController.h文件:

    1.     #import <UIKit/UIKit.h>  
    
    2.       
    
    3.     @interface ViewController : UIViewController{  
    
    4.         IBOutlet UIImageView *imageView;  
    
    5.     }  
    
    6.     @property (nonatomic,retain)IBOutlet UIImageView *imageView;  
    
    7.     @end  

     

    并使xib文件里的imageView与之连接;

    然后是viewController.m文件的实现部分:

      

    1.     @synthesize imageView;  
    
    2.       
    
    3.     CGFloat lastScaleFactor=1;//放大、缩小  
    
    4.     CGFloat  netRotation;//旋转  
    
    5.     CGPoint netTranslation;//平衡  
    
    6.     NSArray *images;//图片数组  
    
    7.     int imageIndex=0;//数组下标  
    
    8.       
    
    9.     - (void)viewDidLoad  
    
    10.   {  
    
    11.       //1、创建手势实例,并连接方法handleTapGesture,点击手势  
    
    12.       UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTapGesture:)];  
    
    13.       //设置手势点击数,双击:点2下  
    
    14.       tapGesture.numberOfTapsRequired=2;  
    
    15.       // imageView添加手势识别  
    
    16.       [imageView addGestureRecognizer:tapGesture];  
    
    17.       //释放内存  
    
    18.       [tapGesture release];  
    
    19.         
    
    20.       //2、手势为捏的姿势:按住option按钮配合鼠标来做这个动作在虚拟器上  
    
    21.       UIPinchGestureRecognizer *pinchGesture=[[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchGesture:)];  
    
    22.       [imageView addGestureRecognizer:pinchGesture];//imageView添加手势识别  
    
    23.       [pinchGesture release];  
    
    24.         
    
    25.       //3、旋转手势:按住option按钮配合鼠标来做这个动作在虚拟器上  
    
    26.       UIRotationGestureRecognizer *rotateGesture=[[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(handleRotateGesture:)];  
    
    27.       [imageView addGestureRecognizer:rotateGesture];  
    
    28.       [rotateGesture release];  
    
    29.         
    
    30.       //4、拖手势  
    
    31.       UIPanGestureRecognizer *panGesture=[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)];  
    
    32.      // [imageView addGestureRecognizer:panGesture];  
    
    33.       [panGesture release];  
    
    34.         
    
    35.       //5、划动手势  
    
    36.       images=[[NSArray alloc]initWithObjects:@"cell.jpg",@"heihua.jpg",@"xuanyi.jpg", nil];  
    
    37.       //右划  
    
    38.       UISwipeGestureRecognizer *swipeGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeGesture:)];  
    
    39.       [imageView addGestureRecognizer:swipeGesture];  
    
    40.       [swipeGesture release];  
    
    41.       //左划  
    
    42.       UISwipeGestureRecognizer *swipeLeftGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeGesture:)];  
    
    43.       swipeGesture.direction=UISwipeGestureRecognizerDirectionLeft;//不设置黑夜是右  
    
    44.       [imageView addGestureRecognizer:swipeLeftGesture];  
    
    45.       [swipeLeftGesture release];  
    
    46.         
    
    47.       //6、长按手势  
    
    48.       UILongPressGestureRecognizer *longpressGesutre=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleLongpressGesture:)];  
    
    49.       //长按时间为1秒  
    
    50.       longpressGesutre.minimumPressDuration=1;  
    
    51.       //允许15秒中运动  
    
    52.       longpressGesutre.allowableMovement=15;  
    
    53.       //所需触摸1次  
    
    54.       longpressGesutre.numberOfTouchesRequired=1;  
    
    55.       [imageView addGestureRecognizer:longpressGesutre];  
    
    56.       [longpressGesutre release];  
    
    57.         
    
    58.       [super viewDidLoad];  
    
    59.       // Do any additional setup after loading the view, typically from a nib.  
    
    60.   }  
    
    61.   //双击屏幕时会调用此方法,放大和缩小图片  
    
    62.   -(IBAction)handleTapGesture:(UIGestureRecognizer*)sender{  
    
    63.       //判断imageView的内容模式是否是UIViewContentModeScaleAspectFit,该模式是原比例,按照图片原时比例显示大小  
    
    64.       if(sender.view.contentMode==UIViewContentModeScaleAspectFit){  
    
    65.           //把imageView模式改成UIViewContentModeCenter,按照图片原先的大小显示中心的一部分在imageView  
    
    66.           sender.view.contentMode=UIViewContentModeCenter;  
    
    67.       }else{  
    
    68.           sender.view.contentMode=UIViewContentModeScaleAspectFit;  
    
    69.       }  
    
    70.   }  
    
    71.   //捏的手势,使图片放大和缩小,捏的动作是一个连续的动作  
    
    72.   -(IBAction)handlePinchGesture:(UIGestureRecognizer*)sender{  
    
    73.       //得到sender捏手势的大小  
    
    74.       CGFloat factor=[(UIPinchGestureRecognizer*)sender scale];  
    
    75.       if(factor>1){  
    
    76.           //图片放大  
    
    77.           sender.view.transform=CGAffineTransformMakeScale(lastScaleFactor+(factor-1), (lastScaleFactor+(factor-1)));  
    
    78.                                                              
    
    79.       }else{  
    
    80.           //缩小  
    
    81.           sender.view.transform=CGAffineTransformMakeScale(lastScaleFactor*factor, lastScaleFactor*factor);  
    
    82.                                                              
    
    83.       }  
    
    84.       //状态是否结束,如果结束保存数据  
    
    85.       if(sender.state==UIGestureRecognizerStateEnded){  
    
    86.           if(factor>1){  
    
    87.               lastScaleFactor+=(factor-1);  
    
    88.           }else{  
    
    89.               lastScaleFactor*=factor;  
    
    90.           }  
    
    91.       }  
    
    92.   }  
    
    93.   //旋转手势  
    
    94.   -(IBAction)handleRotateGesture:(UIGestureRecognizer*)sender{  
    
    95.       //浮点类型,得到sender的旋转度数  
    
    96.       CGFloat rotation=[(UIRotationGestureRecognizer*)sender rotation];  
    
    97.       //旋转角度CGAffineTransformMakeRotation  
    
    98.       CGAffineTransform transform=CGAffineTransformMakeRotation(rotation+netRotation);  
    
    99.       //改变图像角度  
    
    100.      sender.view.transform=transform;  
    
    101.      //状态结束,保存数据  
    
    102.      if(sender.state==UIGestureRecognizerStateEnded){  
    
    103.          netRotation+=rotation;  
    
    104.      }  
    
    105.         
    
    106.  }  
    
    107.  //拖手势  
    
    108.  -(IBAction)handlePanGesture:(UIGestureRecognizer*)sender{  
    
    109.      //得到拖的过程中的xy坐标  
    
    110.      CGPoint translation=[(UIPanGestureRecognizer*)sender translationInView:imageView];  
    
    111.      //平移图片CGAffineTransformMakeTranslation  
    
    112.      sender.view.transform=CGAffineTransformMakeTranslation(netTranslation.x+translation.x, netTranslation.y+translation.y);  
    
    113.      //状态结束,保存数据  
    
    114.      if(sender.state==UIGestureRecognizerStateEnded){  
    
    115.          netTranslation.x+=translation.x;  
    
    116.          netTranslation.y+=translation.y;  
    
    117.      }  
    
    118.        
    
    119.  }  
    
    120.  //划动手势  
    
    121.  -(IBAction)handleSwipeGesture:(UIGestureRecognizer*)sender{  
    
    122.      //划动的方向  
    
    123.      UISwipeGestureRecognizerDirection direction=[(UISwipeGestureRecognizer*) sender direction];  
    
    124.      //判断是上下左右  
    
    125.      switch (direction) {  
    
    126.          case UISwipeGestureRecognizerDirectionUp:  
    
    127.              NSLog(@"up");  
    
    128.              break;  
    
    129.          case UISwipeGestureRecognizerDirectionDown:  
    
    130.              NSLog(@"down");  
    
    131.              break;  
    
    132.          case UISwipeGestureRecognizerDirectionLeft:  
    
    133.              NSLog(@"left");  
    
    134.              imageIndex++;//下标++  
    
    135.              break;  
    
    136.          case UISwipeGestureRecognizerDirectionRight:  
    
    137.              NSLog(@"right");  
    
    138.              imageIndex--;//下标--  
    
    139.              break;  
    
    140.          default:  
    
    141.              break;  
    
    142.      }  
    
    143.      //得到不越界不<0的下标  
    
    144.      imageIndex=(imageIndex<0)?([images count]-1):imageIndex%[images count];  
    
    145.      //imageView显示图片  
    
    146.      imageView.image=[UIImage imageNamed:[images objectAtIndex:imageIndex]];  
    
    147.        
    
    148.  }  
    
    149.  //长按手势  
    
    150.  -(IBAction)handleLongpressGesture:(UIGestureRecognizer*)sender{  
    
    151.      //创建警告  
    
    152.      UIActionSheet *actionSheet=[[UIActionSheet alloc]initWithTitle:@"Image options" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:@"Save Image",@"Copy", nil];  
    
    153.      //当前view显示警告  
    
    154.      [actionSheet showInView:self.view];  
    
    155.      [actionSheet release];  
    
    156.  }  
    
    157.  -(void)dealloc{  
    
    158.      [images release];  
    
    159.      [imageView release];  
    
    160.      [super dealloc];  
    
    161.  }  

      

  • 相关阅读:
    如何免费做一个属于自己稳定有效的图床
    关于自己每日计划是如何制定的
    记一次买4K显示器的心酸历程
    Mac常用的软件推荐
    2019 一整年的总结与一些个人反思
    Lucene学习笔记: 五,Lucene搜索过程解析
    Lucene学习笔记: 四,Lucene索引过程分析
    lucene学习笔记:三,Lucene的索引文件格式
    lucene学习笔记:二,Lucene的框架
    Lucene学习笔记:一,全文检索的基本原理
  • 原文地址:https://www.cnblogs.com/A--G/p/4699727.html
Copyright © 2020-2023  润新知