首先给出文档下载地址 : https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventHandlingiPhoneOS.pdf
Gesture Recognizers 手势识别
总览:
1. 应用可以从用户触摸VIew得到事件.
2. 应用可以从用户移动设备得到事件.
3. 应用可以从用户操作多媒体得到远程控制事件(比如从耳机控制音量)
Gesture Recognizers 手势识别
手势识别是从低级别的事件通过代码转换为高级事件, 当你对一个视图触摸控制的时候,手势识别就会识别判断是哪一种操作, 比如 swipe, pinch, 或者 rotation.如果这个操作被识别了, 手势器会向target发送消息. Target 通常是一个控制器.
使用系统内置的手势识别器比较方便, 你也可以自定义识别器, 会在下面说到.系统识别器如图:
手势识别器是绑定在一个视图上的, 一个视图也可以由多个手势识别器.
手势可分为独立和持续,比如 tap 是独立事件, pinching 捏合 就是持续事件, 手势识别器负责在识别到手势后发送消息给target
创建系统手势识别器需要三步骤:
1. 创建识别器实例, 包括指定target, action, 特殊属性, 如numberOfTapsRequired
2. 将识别器绑定到视图上
3. 实现action的方法并处理事件
手势识别工作方式
他的识别顺序有一系列的状态决定.状态说明定义在 UIGestureRecognizerState 枚举中. 从一个状态到另一个状态是不定的, 取决于当前的情况.下图说明了 独立事件和持续事件的状态转换:
一旦状态到 Recognied 识别器会发送消息给target, 持续时间会多次发送改变的消息给target
手势互动
一个视图上可以有多个手势识别器, 查看这些识别器可以在视图的属性 gestureRecognizers 中查看, 也可以通过 addGestureRecognizer: and removeGestureRecognizer: 添加和移除手势识别器. 通常, 一个视图上的多个手势被识别是无序的,每次识别可能顺序都不一样,你可以通过 UIGestureRecognizer 类的方法, 代理方法 或继承来实现这些行为:
1. 指定一个识别器在另一个识别器之前被识别到.
2. 允许两个识别器同时工作
3. 阻止一个识别器工作
你也可以定义两个手势识别器触发顺序, 通过UIGestureRecognizer 类方法处理
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer; // 直到另一个手势失败的时候才处理本手势
阻止手势识别 使用手势代理方法处理 UIGestureRecognizerDelegate
这两个代理方法都可以达到阻止手势的目的
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
当触摸开始,并且你能立即判断哪个手势是否需要开始, 可以使用gestureRecognizer:shouldReceiveTouch: 代理方法, 每次有新的触摸都会调用该方法如果需要等待足够长时间才能判断哪个手势是否开始,可以使用gestureRecognizerShouldBegin: 代理方法, 该方法是在识别器将要从状态 possible 转换至下一个状态是调用.
允许两个手势同时被识别
默认两个识别器不能再同一时刻被识别,但是,假设你想使用放大和旋转同时进行,就需要进行配置了.可以使用代理方法
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: 来设置.
与其他界面控制控件交互
在iOS6及以后,默认的系统控制控件的手势不能被覆盖.比如,默认UIButton的点击事件是一个 single tap , 如果你有自己定义的一个 single tap 手势与UIButton 绑定,点击button后 button的action 方法会调用, 而不是single tap 手势触发.类似的还有很多, 比如在UISlider 上添加 swip 手势, 在UISwitch 上添加 pan 手势. 当然, 也可以继承系统组件来更改事件响应.
一个事件包含了所有当前多点触摸的一个序列, 一个手势可以有一个或多个用 UITouch 对象代表的触摸.比如: 一个捏合关闭的手势包含了两个触摸--两个手指在屏幕上向相反方向滑动. 一个多点触摸是由 从手指接触到屏幕开始, 到最后一个手指离开屏幕后之间的时间组成的序列. 当手指在屏幕上移动的时候, 系统会发送 用UITouch对象代表的事件, 多点触摸时间由 UITouch 的 type UIEventTypeTouches 代表. 每一个UITouch 对象只能追踪一个单一手指的活动, 在手指滑动中, UIKit 会更新UITouch的属性,这些属性包括 触摸的阶段, 在触摸View上的位置, 它之前的位置和时间戳.
一个触摸的阶段标志的触摸何时开始, 何时移动或静止, 何时结束, 下图为触摸的阶段图
当有触摸改变时候, 系统会调用下面的方法通知应用
● touchesBegan:withEvent: method when one or more fingers touch down on the screen.
● touchesMoved:withEvent: method when one or more fingers move.
● touchesEnded:withEvent: method when one or more fingers lift up from the screen.
● touchesCancelled:withEvent: method when the touch sequence is canceled by a system event, such as an incoming phone call.
每个方法都和一个触摸阶段关联
调整UITouch在UIView 之间的传递及响应
可能有许多时候你需要一个View 在手势识别器识别之前 接收到Touch 事件, 但是当你在更改Touch传递顺序之前, 最好了解一下系统默认的操作行为.
默认触摸事件从 UIApplication 传递到UIWindow对象, UIWindow 将事件发送至任何一个绑定在被触摸到的View上的手势识别器.
UIWindow 会延迟touch对象到View去, 这样可以在这段延迟期间识别器可以识别手势,如果手势被识别到, 那么UIWindow将不会传递任何touch对象到这个view 上了. 并且取消之前发送到这个view上的touch序列.
假设有一个不连续的手势需要两个手指触摸,这将会转化成两个分开的touch对象,当触摸开始, touch对象从应用传到window里的view
这个 touch消息传递的序列如下:
1. window 在began 阶段发出两个touch对象 - 通过touchesBegan:withEvent: 方法去识别, 识别器此时还没有识别, 所以 状态为 Possible. window发送这些消息给绑定手势触发器的view.
2.window 在 move 阶段发出两个touch 对象- 通过touchesMoved:withEvent: 方法去识别, 识别器仍然没有识别到, 所以状态为 Possible.消息会传到view.
3.window 在ended 阶段 发出一个touch 对象 - 通过touchesEnded:withEvent: 方法去识别, 识别器没有因不足的信息而放弃, 但是消息不会传到view上了.
4.window在ended 阶段 发出了另一个touch对象, 这时候触发器触发了,找到了手势, 所以状态改为 recognized . view调用touchesCancelled:withEvent: 方法取消前一个touch对象触发方法, 这两个touch对象最终阶段为 canceled.
影响touch对象传递到view
你可以改变几个UIGestureRecognizer 类的属性值, 从而改变默认的touch传递路径,如果你改变了以下属性的默认值, 你将得到不同的行为:
- 属性 @property(nonatomic)BOOL delaysTouchesBegan; 默认为NO -- 通常,window 在began 和move 阶段发送touch对象到view上, 如果设置delaysTouchesBegan 为YES, 它将会阻止
- 属性 @property(nonatomic)BOOL delaysTouchesEnded; 默认为YES -- 当设置为YES的时候, 它确保当手势取消后, view不会响应事件, 当识别器在识别过程中, window不会传递事件对象给绑定的view.如果识别器识别到手势,它将取消touch事件, 如果没有识别到手势, 将会触发 -touchedEnded:withEvent 方法. 如果设置NO, 就允许了识别touch时间和手势同时触发. 举个例子: 一个view绑定了 tap 手势, 并且需要2次点击. 当我们双击的时候, 如果设置为YES, 触发方法顺序为 1. touchesBegin:withEvent: 2. touchedBegin:withEvent 3.touchesCancelled:withEvent 4.touchesCancelled:withEvent: 如果设置了NO, 触发方法顺序为: 1. touchesBegin:withEvent: 2.touchesEnded:withEvent: 3.touchesBegin:withEvent: 4.touchedCancelled:withEvent.
- (void)reset;
- (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;
重置手势识别器的状态 state
当我们的识别器的识别状态改变(UIGestureRecognizerState)时并且阻止了继续识别的时候,识别器会调用 -reset 方法来重置内部状态