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;取出 特定视图或者窗口上的触碰对象。在这几个事件中,都可以拿到触碰对象,然后根据其位置、状态、时间属性做逻辑处理。
轻击操作很容易引起歧义,比如用户点击了一次之后,并不知道用户是想单击还是只是双击的一部分,或者点了两次之后并不知道用户是想双击还是继续点击。可以使用延迟调用函数解决这个问题。
- -(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,单位是像素。
借用一个项目来说明: http://blog.csdn.net/chang6520/article/details/7924313
首先新建一个基于Sigle view Application的项目,名为GestureTest;我的项目结构如下:
往viewController.xib文件里拖动一个imageView,并使覆盖整个屏幕,改动属性为:
viewController.h文件:
. #import <UIKit/UIKit.h> . . @interface ViewController : UIViewController{ . IBOutlet UIImageView *imageView; . } . @property (nonatomic,retain)IBOutlet UIImageView *imageView; . @end
并使xib文件里的imageView与之连接;
然后是viewController.m文件的实现部分:
. @synthesize imageView; . . CGFloat lastScaleFactor=;//放大、缩小 . CGFloat netRotation;//旋转 . CGPoint netTranslation;//平衡 . NSArray *images;//图片数组 . int imageIndex=;//数组下标 . . - (void)viewDidLoad . { . //1、创建手势实例,并连接方法handleTapGesture,点击手势 . UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTapGesture:)]; . //设置手势点击数,双击:点2下 . tapGesture.numberOfTapsRequired=; . // imageView添加手势识别 . [imageView addGestureRecognizer:tapGesture]; . //释放内存 . [tapGesture release]; . . //2、手势为捏的姿势:按住option按钮配合鼠标来做这个动作在虚拟器上 . UIPinchGestureRecognizer *pinchGesture=[[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchGesture:)]; . [imageView addGestureRecognizer:pinchGesture];//imageView添加手势识别 . [pinchGesture release]; . . //3、旋转手势:按住option按钮配合鼠标来做这个动作在虚拟器上 . UIRotationGestureRecognizer *rotateGesture=[[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(handleRotateGesture:)]; . [imageView addGestureRecognizer:rotateGesture]; . [rotateGesture release]; . . //4、拖手势 . UIPanGestureRecognizer *panGesture=[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)]; . // [imageView addGestureRecognizer:panGesture]; . [panGesture release]; . . //5、划动手势 . images=[[NSArray alloc]initWithObjects:@"cell.jpg",@"heihua.jpg",@"xuanyi.jpg", nil]; . //右划 . UISwipeGestureRecognizer *swipeGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeGesture:)]; . [imageView addGestureRecognizer:swipeGesture]; . [swipeGesture release]; . //左划 . UISwipeGestureRecognizer *swipeLeftGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeGesture:)]; . swipeGesture.direction=UISwipeGestureRecognizerDirectionLeft;//不设置黑夜是右 . [imageView addGestureRecognizer:swipeLeftGesture]; . [swipeLeftGesture release]; . . //6、长按手势 . UILongPressGestureRecognizer *longpressGesutre=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleLongpressGesture:)]; . //长按时间为1秒 . longpressGesutre.minimumPressDuration=; . //允许15秒中运动 . longpressGesutre.allowableMovement=; . //所需触摸1次 . longpressGesutre.numberOfTouchesRequired=; . [imageView addGestureRecognizer:longpressGesutre]; . [longpressGesutre release]; . . [super viewDidLoad]; . // Do any additional setup after loading the view, typically from a nib. . } . //双击屏幕时会调用此方法,放大和缩小图片 . -(IBAction)handleTapGesture:(UIGestureRecognizer*)sender{ . //判断imageView的内容模式是否是UIViewContentModeScaleAspectFit,该模式是原比例,按照图片原时比例显示大小 . if(sender.view.contentMode==UIViewContentModeScaleAspectFit){ . //把imageView模式改成UIViewContentModeCenter,按照图片原先的大小显示中心的一部分在imageView . sender.view.contentMode=UIViewContentModeCenter; . }else{ . sender.view.contentMode=UIViewContentModeScaleAspectFit; . } . } . //捏的手势,使图片放大和缩小,捏的动作是一个连续的动作 . -(IBAction)handlePinchGesture:(UIGestureRecognizer*)sender{ . //得到sender捏手势的大小 . CGFloat factor=[(UIPinchGestureRecognizer*)sender scale]; . if(factor>){ . //图片放大 . sender.view.transform=CGAffineTransformMakeScale(lastScaleFactor+(factor-), (lastScaleFactor+(factor-))); . . }else{ . //缩小 . sender.view.transform=CGAffineTransformMakeScale(lastScaleFactor*factor, lastScaleFactor*factor); . . } . //状态是否结束,如果结束保存数据 . if(sender.state==UIGestureRecognizerStateEnded){ . if(factor>){ . lastScaleFactor+=(factor-); . }else{ . lastScaleFactor*=factor; . } . } . } . //旋转手势 . -(IBAction)handleRotateGesture:(UIGestureRecognizer*)sender{ . //浮点类型,得到sender的旋转度数 . CGFloat rotation=[(UIRotationGestureRecognizer*)sender rotation]; . //旋转角度CGAffineTransformMakeRotation . CGAffineTransform transform=CGAffineTransformMakeRotation(rotation+netRotation); . //改变图像角度 . sender.view.transform=transform; . //状态结束,保存数据 . if(sender.state==UIGestureRecognizerStateEnded){ . netRotation+=rotation; . } . . } . //拖手势 . -(IBAction)handlePanGesture:(UIGestureRecognizer*)sender{ . //得到拖的过程中的xy坐标 . CGPoint translation=[(UIPanGestureRecognizer*)sender translationInView:imageView]; . //平移图片CGAffineTransformMakeTranslation . sender.view.transform=CGAffineTransformMakeTranslation(netTranslation.x+translation.x, netTranslation.y+translation.y); . //状态结束,保存数据 . if(sender.state==UIGestureRecognizerStateEnded){ . netTranslation.x+=translation.x; . netTranslation.y+=translation.y; . } . . } . //划动手势 . -(IBAction)handleSwipeGesture:(UIGestureRecognizer*)sender{ . //划动的方向 . UISwipeGestureRecognizerDirection direction=[(UISwipeGestureRecognizer*) sender direction]; . //判断是上下左右 . switch (direction) { . case UISwipeGestureRecognizerDirectionUp: . NSLog(@"up"); . break; . case UISwipeGestureRecognizerDirectionDown: . NSLog(@"down"); . break; . case UISwipeGestureRecognizerDirectionLeft: . NSLog(@"left"); . imageIndex++;//下标++ . break; . case UISwipeGestureRecognizerDirectionRight: . NSLog(@"right"); . imageIndex--;//下标-- . break; . default: . break; . } . //得到不越界不<0的下标 . imageIndex=(imageIndex<)?([images count]-):imageIndex%[images count]; . //imageView显示图片 . imageView.image=[UIImage imageNamed:[images objectAtIndex:imageIndex]]; . . } . //长按手势 . -(IBAction)handleLongpressGesture:(UIGestureRecognizer*)sender{ . //创建警告 . UIActionSheet *actionSheet=[[UIActionSheet alloc]initWithTitle:@"Image options" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:@"Save Image",@"Copy", nil]; . //当前view显示警告 . [actionSheet showInView:self.view]; . [actionSheet release]; . } . -(void)dealloc{ . [images release]; . [imageView release]; . [super dealloc]; . }
UIView用户事件响应的更多相关文章
- iOS学习笔记(2)— UIView用户事件响应
UIView除了负责展示内容给用户外还负责响应用户事件.本章主要介绍UIView用户交互相关的属性和方法. 1.交互相关的属性 userInteractionEnabled 默认是YES ,如果设置为 ...
- iOS事件响应链
首先,当发生事件响应时,必须知道由谁来响应事件.在IOS中,由响应者链来对事件进行响应,所有事件响应的类都是UIResponder的子类,响应者链是一个由不同对象组成的层次结构,其中的每个对象将依次获 ...
- 追踪app崩溃率、事件响应链、Run Loop、线程和进程、数据表的优化、动画库、Restful架构、SDWebImage的原理
1.如何追踪app崩溃率,如何解决线上闪退 当 iOS设备上的App应用闪退时,操作系统会生成一个crash日志,保存在设备上.crash日志上有很多有用的信息,比如每个正在执行线程的完整堆栈 跟踪信 ...
- iOS-UIResponse之事件响应链及其事件传递
UIResponse之事件响应链及其事件传递 我们的App与用户进行交互,基本上是依赖于各种各样的事件.一个视图是一个事件响应者,可以处理点击等事件,而这些事件就是在UIResponder类中定义的. ...
- 【iOS 开发】iOS 开发 简介 (IOS项目文件 | MVC 模式 | 事件响应机制 | Storyboard 控制界面 | 代码控制界面 | Retina 屏幕图片适配)
一. iOS 项目简介 1. iOS 文件简介 创建一个 HelloWorld 项目, 在这个 IOS 项目中有四个目录 : 如下图; -- HelloWorldTests 目录 : 单元测试相关的类 ...
- 关于UIView用户交互相关的属性和方法
UIView除了负责展示内容给用户外还负责响应用户事件 1.交互相关的属性 userInteractionEnabled 默认是YES ,如果设置为NO则不响应用户事件,并且把当前控件从事件队列中删除 ...
- iOS中的事件响应链、单例模式、工厂模式、观察者模式
学习内容 欢迎关注我的iOS学习总结--每天学一点iOS:https://github.com/practiceqian/one-day-one-iOS-summary iOS中事件传递和相应机制 i ...
- DuiLib事件分析(一)——鼠标事件响应
最近在处理DuiLib中自定义列表行元素事件,因为处理方案得不到较好的效果,于是只好一层一层的去剥离DuiLib事件是怎么来的,看能否在某一层截取消息,自己重写. 我这里使用CListContaine ...
- Android MotionEvent事件响应机制
在android中,事件主要包括点击.长按.拖曳.滑动等操作,这些构成了Android的事件响应,总体来说,所有的事件都由如下三个部分作为基础构成: 按下(action_down),移动(action ...
随机推荐
- Volley框架支持HTTPS请求。
第一次写帖子,嘿嘿. 最近了解到google2013IO大会出了个网络框架,正好项目也需要用到,就看了下. 最后发现接口都是HTTPS的,但是Volley默认是不支持HTTPS,网上找了好久,都没有对 ...
- [LeetCode#271] Encode and Decode Strings
Problem: Design an algorithm to encode a list of strings to a string. The encoded string is then sen ...
- bzoj3790
观察发现,这道题目其实就相当于一个最小区间覆盖问题这里的区间是指以每个点为中心的最长回文串很久没写manacher,有点感动不得不说manacher是一个非常好的算法 ..] of char; c,l ...
- 我要爱死这个markdown 了
今天上班依旧看wpdang的文章,最后作者说,文章使用markdown写的,好奇心促使我搜了一把什么是markdown.然后看到了这篇文章,一瞬间就开始兴奋了.顿时觉得,这个东西太好用 了,简直又激起 ...
- Light OJ 1032 - Fast Bit Calculations(数位DP)
题目大意: 一个数字把他看成二进制数字,数字里又会一些相邻的1,问从0到n至间所有相邻1的总和是多少? 分解成2进制数字,然后数位DP就行了. ======================== ...
- JDK 1.6.0和 6.0 有啥区别,JavaTM SE 6 的平台名字和版本号的说明(转)
一直这么理解,今天才看到官方的解释,真是有点汗颜. 核心就是 6.0用于平台和产品的名字,而1.6.0用于开发者. 他们指的是同一个东西. 原文地址:http://java.sun.com/javas ...
- IP定位 C#
IP定位 已经不是什么新的技术,但是在做项目中却会常常用到.找网上找了许久,也做了许多的实验,觉得QQwry.dat,很很好用的,作者也提供了开发的源码和大家分享. 在这里感谢作者.我在项目中也用到了 ...
- unity3d 雪与沙的渲染
很简单的一个shader,跟着上一篇地形顺便弄的 方法就是基于物理的diffuse,再加上noise权重的specular 公式参考 JOURNEY JOURNEY中认为OrenNayar比较浪费,所 ...
- Oracle 插入超4000字节的CLOB字段的处理方法
最近在做系统开发的时候需要想Oracle数据库插入超过4000字节的CLOB字段,在网上查询了N久才发现下面的解决方案,故留存以备后查. 我们可以通过创建单独的OracleCommand来进行指定的插 ...
- android 国内sdk下载地址及代理, android 环境搭建
http://www.androiddevtools.cn/ http://www.cnblogs.com/bjzhanghao/archive/2012/11/14/android-platform ...