1.触摸事件处理
iOS程序启动后UIApplication会循环监听用户操作,当有触摸操作时,触摸事件会被添加到UIApplication事件队列,
UIApplication会从事件队列取出最前面的事件并分发处理,通常先分发给应用程序主窗口,主窗口会
调用hitTest:withEvent:方法(UIView的方法),查找合适的事件触发视图
- 在顶级视图(key window的视图)上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;
- 如果返回NO,那么A返回nil;
- 如果返回YES,那么它会向当前视图的所有子视图(key window的子视图)发送hitTest:withEvent:消息,遍历所有子视图的顺序是从subviews数组的末尾向前遍历(从界面最上方开始向下遍历)。
- 如果有subview的hitTest:withEvent:返回非空对象则A返回此对象,处理结束(注意这个过程,子视图也是根据pointInside:withEvent:的返回值来确定是返回空还是当前子视图对象的。并且这个过程中如果子视图的hidden=YES、userInteractionEnabled=NO或者alpha小于0.1都会并忽略);
- 如果所有subview遍历结束仍然没有返回非空对象,则A返回顶级视图;
当一个事件发生后首先看initial view(事件触发所在视图)能否处理这个事件,如果不能则会将事件传递给其上级视图(inital view的superView);如果上级视图仍然无法处理则会继续往上传递;一直传递到视图控制器view controller,首先判断视图控制器的根视图view是否能处理此事件;如果不能则接着判断该视图控制器能否处理此事件,如果还是不能则继续向上传递;(对于第二个图视图控制器本身还在另一个视图控制器中,则继续交给父视图控制器的根视图,如果根视图不能处理则交给父视图控制器处理);一直到window,如果window还是不能处理此事件则继续交给application(UIApplication单例对象)处理,如果最后application还是不能处理此事件则将其丢弃。
!!!以下几种情况不能处理触摸事件:
1.userInteractionEnabled=NO
2.hidden=YES
3.alpha=0~0.01
注意!!!
UIResponse的子类都能对触摸事件进行处理,
处理方法如:
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;
这些处理方法的默认实现是将事件传递给响应链的下一个,如果我们实现了其中一个事件处理方法,
但没有把它传递给它的下一个响应链,那么下面的响应链都不会处理该事件,例如我们实现了当前View的触摸开始处理方法,
但没有将事件传给下一个响应者,那么下一个相应者不能处理触摸开始事件。
2.手势识别
iOS的手势识别功能真的是太赞了,一共有六中常用手势:
常用属性
常用方法
在iOS中手势有不同的状态,我们可以根据不同状态做不同的处理
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
// 尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态
UIGestureRecognizerStatePossible,
// 手势已经开始,此时已经被识别,但是这个过程中可能发生变化,手势操作尚未完成
UIGestureRecognizerStateBegan,
UIGestureRecognizerStateChanged, // 手势状态发生转变
UIGestureRecognizerStateEnded, // 手势识别操作完成(此时已经松开手指)
UIGestureRecognizerStateCancelled, // 手势被取消,恢复到默认状态
UIGestureRecognizerStateFailed, // 手势识别失败,恢复到默认状态
// 手势识别完成,同UIGestureRecognizerStateEnded
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
};
下面解释下为什么要定义状态枚举
对于离散型手势(既简单手势)UITapGestureRecgnizer要么被识别,要么失败,点按(假设点按次数设置为1,并且没有添加长按手势)下去一次不松开则此时什么也不会发生,松开手指立即识别并调用操作事件,并且状态为3(已完成)。
但是连续手势要复杂一些,就拿旋转手势来说,如果两个手指点下去不做任何操作,此时并不能识别手势(因为我们还没旋转)但是其实已经触发了触摸开始事件,此时处于状态0;如果此时旋转会被识别,也就会调用对应的操作事件,同时状态变成1(手势开始),但是状态1只有一瞬间;紧接着状态变为2(因为我们的旋转需要持续一会),并且重复调用操作事件(如果在事件中打印状态会重复打印2);松开手指,此时状态变为3,并调用1次操作事件。
使用手势
手势识别这么好玩这么强大,那么我们该怎么使用呢,大概有以下几步就搞定了:
----->创建对应的手势对象;
---->设置手势识别属性【可选】;
---->把手势添加到指定的对象;
---->编写手势处理方法;
解决手势冲突
手势这么多,有时候我们可能给一个控件添加了多个手势,不同的手势响应不同的处理,
如果一个手势A的识别部分是另一个手势B的子部分时,默认情况下A就会先识别,B就无法识别了。
要解决这个冲突可以利用
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
方法来完成。