面试被问到了一个Button,如果点击无效果,你会怎么排查? 这个问题我当时回答的比较简单,但是面试官追问的比较深,我回答的有些含糊,课后补习一下。
前言:
借鉴了一些简书大佬的文章的文章,然后自己写了Demo,测试完毕后自己总结下自己的理解。
iOS中的事件可以分为3大类型:触摸事件-加速计事件-远程控制事件。
此文只针对触摸事件。
查看源码可以很轻易发现:
UIViewController继承关系 UIView继承关系 UIWindow继承关系 UIApplication继承关系由此可见:UIViewController - UIView - UIWindow - UIApplication都是继承UIResponder,那UIResponder又是什么呢,图中都继承UIResponder,能代表又有什么结论呢?
先说说UIResponder。
在iOS中UIResponder类是专门用来响应用户的操作处理各种事件的,包括触摸事件(Touch Events)、运动事件(Motion Events)、远程控制事件(Remote Control Events)。具体的UIResponder内部又是如何处理事件,这里不做说明。
查看UIResponder源码可以发现:
UIResponder事件官方由此可见,官方让我们重新这几个方法,就可以实现触摸相关的活动事件。
UIView,UIApplication,UIWindow,UIViewController继承UIResponder,同样具有父类的这些触摸事件。
这里以UIViewController和UIView为例,其他的道理是相同的。
在UIViewController重新touchesBegan方法,点击当前的Controller,
直接上代码,因为代码比较简单,我就截图说明。
在UIViewController实现touchesBegan方法触摸事件参数有两个UITouch,UIEvent,两个参数有着自己不同的任务。
UITouch
当用户用一根手指触摸屏幕时,会创建一个与手指相关的UITouch对象.
UITouch对象,封装了当前的Touch事件的对象集合。
UITouchd对象的属性官方介绍Touch对象保存着跟手指相关的信息,比如触摸的位置、时间、阶段
当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置
当手指离开屏幕时,系统会销毁相应的UITouch对象。
重写:touchesBegan
点击一次,当前操作只有一次,所以Set集合只有一个Touch事件,而且事件tap count = 1。(单击事件)。
重写:touchesMoved
随着手指的拖动,set集合的Touch对象会随之拖到而改变,且此时的Touch对象是不一样的,当前移动的位置和上一次不一样。
正因为手指在不断的移动,所以当前的点和上次移动的点,官方也给了相关方法来获取。
获取当前手指移动和上次移动的点UIEvent
UIEvent事件触摸的目的是生成触摸事件供响应者响应,一个触摸事件(UITouch)对应一个UIEvent对象,其中的type属性标识了事件的类型,事件有如下几种类型:
根据手势的不同操作(单击,滑动,按压,平移等动作),所出发的事件效果会不同。
枚举事件类型回到前面说的Button的案例。
有个UIButton,Button的点击事件我们也实现了,却点击无效,无法响应与之对应的点击方法,这时候我们该如何去查?
一般都会检查自己写addTarget是否有误,点击事件ButtonClick是否实现。
其次我们也可以从View的事件传递考虑。
View事件传递有三个原则不会传递:
1.当前View或者控件的userInteractionEnabled = NO;
2.当前View或者控件的Hidden为YES
3.同理当前View或者控件透明的<=0.01,也就是(0-0.01)。
除此之外,我们也可以通过重写View的hitTest来查看当前事件可传递的View。
View的hitTest方法这里使用下比较经典的一个图解:
事件传递举例:
点击黄色View,因为我们并没有写以上阻断事件传递的三种方式,所以黄色的父控件(蓝色),父父控件(橙色),以及父父父控件(白色)打印的结果都是一直的,指向黄色View。
应用如何找到最合适的控件来处理事件?
1.首先判断主窗口(keyWindow)自己是否能接受触摸事件
2.触摸点是否在自己身上
3.从后往前遍历子控件,重复前面的两个步骤(首先查找数组中最后一个元素)
4.如果没有符合条件的子控件,那么就认为自己最合适处理
上面的说法是比较官方的解释,用比较通俗的解释就是说:
UIApplication 问 keyWindow :小子,我给你个事件你能不能处理下?
keyWindow唯唯诺诺的说:我。。我不能,触摸点不在我身上。
keyWindow 又去问它的子控件橙色View。
keyWindow:小橙,老大安排了事情,我不能处理,你小子能不能给处理这个点击事件?
OrangeView同样唯唯诺诺的说:我不能,触摸点不在我身上。
。。。。。。
直到最后一层黄色View。
OrangeView:没问题啦~触摸点在我身上,老大们放心吧,我会处理好的(触摸事件里的操作)。
大概用比较欢喜的理解就是这样传递。
特殊情况:
hitTest:withEvent:中return nil的意思是调用当前hitTest:withEvent:方法的view不是合适的view,子控件也不是合适的view。如果同级的兄弟控件也没有合适的view,那么最合适的view就是父控件。
1.所有的子控件都有拦截事件传递的情况,比如都给拦截掉了。
你们他们最终的父控件会返回nil,代表没有其子控件可以处理,只能自己处理了。
子控件打印:
父控件会返回nil情况父控件打印:
GrayView其实就是上图的1白色,我demo命名不同代表只能自己去处理这个事件。
其次还需要一点说明,如果1234中的某个view拦截事件了,就不会传递给其子控件啦。
3有拦截事件功能,4就不能操作这个事件,只能是3的父,父父,父父父View来处理。
以上就是我对View事件和传递的自己的理解,记录一下,作为知识点储存。
如果有误或者更好的理解,望大家留言,谢谢。
网友评论