前言
素日咱们在玩手机,当咱们的手指点击的当人手在点击屏幕时,体系会依据咱们的手指动作产生一个触屏事情,这个事情能够是点击、拖动、缩放等手势,咱们统称为触屏事情。那么体系是如何依据触屏事情去呼应咱们想要的成果呢?
一、根本元素了解
想要知道体系是怎样呼使用户的触屏事情,需求提早知道3个与用户界面事情相关的类,它们别离是UITouch
、UIEvent
和UIResponder
UITouch
一个手指接触会生成一个UITouch目标,多个手指接触会生成多个UITouch目标。查看官方文档,其界说如下:
图中的特点和意义别离表明
-
timestamp
:接触事情产生的时刻戳,单位是秒(s) -
phase
:接触事情的阶段,即接触事情的状况(例如began、end、cancel
) -
tapCount
:短时刻内接触的点击次数,用来判别单击,双击等 -
type
:接触类型,例如手指接触或笔接触(ios9.0可用) -
majorRadius
:接触的主轴半径,一般用于辨认接触点的大小和形状(ios8.0可用) -
majorRadiusTolerance
:接触的主轴半径容差,一般用于确认接触点的大小和形状的变化规模(ios8.0可用) -
window
:接触地点的窗口 -
view
:接触地点的视图 -
gestureRecognizers
:是一个数组,包含了与当时接触点相关的一切手势辨认器目标,但是并不包含其他视图或窗口中的手势辨认器目标(ios3.2可用)
UIEvent
UIEvent
是一切用户界面事情的基类,包含触屏事情、手势事情、按键事情等,每个UIEvent
供给事情类型、事情产生时刻、事情涉及到的接触目标等信息。UITouch
目标则是UIEvent
目标中的一部分,表明了用户当时的触屏信息。它首要包含以下特点:
-
type
:表明事情的类型,例如接触、按压、滚动 -
subtype
:表明事情的子类型,用于进一步描述事情的类型。例如上面的type
的值是接触事情,那么subtype
能够表明接触事情的详细类型,例如单击、双击、长按等 -
timestamp
:表明事情产生的时刻戳,,单位是秒(s) -
modifierFlags
:表明事情产生时键盘上的修饰键状况,如 Shift、Control、Option 等 -
buttonMask
:表明接触事情中的按下的按钮掩码。在接触事情中,用户可能会按下屏幕上的多个按钮,例如 Home 按钮、电源按钮等,buttonMask 特点能够表明这些按钮的状况 -
allTouches
:表明事情涉及到的一切接触目标,里面是一个由UITouch
组成的列表
UIResponder
UIResponder
是 iOS 中呼应者目标的基类,它界说了一些办法和特点,用于呼应输入事情和接触事情,并将事情传递给后续的呼应者目标。 它包含以下特点:
-
nextResponder
:下一个呼应者目标,也是一个UIResponder
目标。在呼应事情的过程中,假如当时目标无法处理事情,体系会将该事情传递给nextResponder
目标处理。假如nextResponder
为nil
,则表明当时目标已经是呼应链的末尾 -
canBecomeFirstResponder
:当时目标是否能成为第一呼应者,只读 -
canResignFirstResponder
:当时目标是否能够抛弃第一呼应者的身份,只读 -
isFirstResponder
:,当时目标是否是第一呼应者,只读
二、触屏事情的根本元素相关
介绍完触屏事情的根本元素的三个类,咱们来说一下它们的相关
首先,在iOS使用程序中,事情一般是由UIApplication
目标分发给UIResponder
目标去处理的。当用户接触屏幕,产生触屏事情,UIApplication
目标会创立一个UIEvent
目标(UITouch
目标则是UIEvent
目标中的一部分),然后将这个UIEvent
目标发送给当时呼应事情的UIResponder
目标。
然后,假如当时呼应事情的UIResponder
目标无法处理该事情,则会将事情传递给父视图或父控制器,知道有一个目标能成功处理该事情,否则,事情最终抵达回到了UIApplication
目标则会被丢弃。
最终,咱们能够经过重写UIResponder
的父类办法来呼使用户事情。例如-touchesBegan:withEvent:
办法,这个办法会在用户开端接触屏幕时被调用,咱们能够重写它,经过判别接触事情的方位,执行相应操作,比方弹出定制显示框。
三、触屏事情深入到到使用
在 iOS 使用程序中,当咱们接触屏幕时,体系会将接触事情参加UIApplication
目标的办理行列事情中,然后UIApplication
目标会取出行列里最先进去的事情(FIFO),发出去处理,一般是传递给最上层的窗口目标(UIWindow
类),UIWindow
类经过 hitTest:withEvent:
办法来确认哪个视图目标(一同也是呼应者UIResponder
),来负责处理该事情。因此,hitTest:withEvent:
办法是触屏事情和视图目标的要害联系点。
hitTest:withEvent:
是 UIView
类的办法,该办法会递归地遍历视图层次结构,找到最合适的视图目标(UIResponder
)来处理接触事情,其寻觅规律如下图:
上图是一幅依照数字从小到大增加进去的视图目标
举个例子:
1. 点击赤色view,寻觅次序是:
白色view(找到,去子视图)-> 黄色view(找不到,去同级视图)-> 赤色view(找到,没有子视图,回来自己)
2. 点击蓝色view,寻觅次序是:
白色view(找到,去子视图)-> 黄色view(找到,去子视图)-> 紫赤色view(找不到,去同级视图)-> 绿色view(找不到,去同级视图)-> 蓝色view(找到,没有子视图,回来自己)
3. 蓝色view躲藏,点击蓝色view的方位,寻觅次序是:
白色view(找到,去子视图)-> 黄色view(找到,去子视图)-> 紫赤色view(找不到,去同级视图)-> 绿色view(找不到,去同级视图)-> 蓝色view(蓝色view躲藏了,回来nil,找不到,回来)->顺着呼应者链回到黄色view,然后回来黄色view
从现象能够分分出:
- 调用次序是先从父视图逐级向其子视图遍历调用,假如视图在同级,就依照同级视图增加到这个父视图上的次序,从后向前遍历,即后增加的先遍历(FILO)
- 每一个被遍历到的视图,会调用
-pointInside:withEvent:
办法判别点击事情是不是在本视图,假如不是,则回来NO
,就不会遍历其子视图。假如是,则回来YES
,就会遍历其子视图,先调用其子视图的-hitTest:withEvent:
办法,然后该子视图又会调用-pointInside:withEvent:
办法判别点击事情是不是在本视图,如此递归下去。 - 假如某个视图的
-hitTest:withEvent:
办法回来了呼应视图(呼应者),则会停止遍历还没遍历的视图。 - 假如视图不接受交互 :
userInteractionEnabled = NO;
,或许设置了躲藏:hidden = YES;
,或许透明度: alpha<=0.01 , 则不会呼应接触事情,会直接在-hitTest:withEvent:
办法回来nil
,并不会调用-pointInside:withEvent:
办法
四、关于呼应流程
前面介绍了接触事情的传递过程,那么找到呼应者之后,呼应者是如何呼应事情的呢?
在 iOS 使用程序中,当-hitTest:withEvent:
办法回来了呼应视图,体系会将接触事情传递给该视图目标的touches
办法来处理事情,它们别离是:
//接触事情的开端
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//接触事情的移动
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//接触事情的结束
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//接触事情的撤销
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
在上面四个办法中,touchesBegan
和touchesEnded
表明接触事情的开端和结束,是一定会调用到的,至于移动和撤销需求依据手势和其他状况才会触发。touches
办法来处理完事情后,体系会把接触事情UIEvent
打包发送给呼应者的呼应办法,其流程如下图:
留意:假如呼应者无法呼应事情,那么体系会顺着呼应者链从下往上找,直至找到最终的呼应者
五、总结
熟悉iOS接触事情传递的机制和流程,能够发明很多匪夷所思的功能,例如点击A控件,让B控件呼应事情,或许点击A控件,A控件以及A控件的父控件一同呼应事情。还需求留意的是,手势的辨认优先级是高于点击事情的,这方面的剖析等我找天有时刻再做个分享。