怎样选择最合适处理事件的控件
-
当用户点击屏幕后,产生2个参数 一个
touches
保存用户点击的UITouch
对象到NSSet
中,和一个事件对象UIEvent
,并且加入到UIApplication
对象的事件处理Loop
中队列 -
由UIApplication查看,当前有没有堵塞,如果没有就将事件分发下去,一般是交给主窗口,keyWindow
-
然后Window 会用 下面的这个方法
//Swift
func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView?
//Objective-C
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- 该方法是用于查询当前最佳处理事件的控件,如果我们重写
window
的这个方法后,其他view
的触摸事件没受影响,那么说明我们的思路是对的,废话不多说,代码带注释
Swift
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
/*1.判断自己可不可以与用户交互满足一下几点
1.self.userInteractionEnabled = YES 可以与用户交互
2.self.hidden = NO 不是隐藏的
3.self.alpha > 0.01 透明度大于 0.01 也就是用户可见
*/
//下面是写的上面的反面
if self.userInteractionEnabled == false || self.hidden == true || self.alpha <= 0.01 {
return nil
}
/*2.这里查看这个点是不是在控件的bound上了,如果不在就返回nil */
if self.pointInside(point, withEvent: event) == false { return nil }
/*3.遍历 子控件,由于addSubView 本身是把图层加到另
一个图层上面,导致图层的顺序是最后添加的在最上面,也就是
上面说的,后面加入的控件可能会在盖住,前面加入的控件图
层,而响应链是优先最上面的图层的,考虑到这个递归算法是
DFS 也就是深度优先,所以必须从后面的子控件遍历
*/
//3.1得到子控制总个数
let count = Int(self.subviews.count) - 1
for var i = count ; i >= 0 ; --i {
//1.得到子控件
let childView:UIView = self.subviews[i]
/*2,将父控件的坐标点转换到子控件中的形式,其实就拿
子控件在父控件的frame的x,y ,与用户触点的父控件的
x,y,进行减法*/
let childPoint = self.convertPoint(point, toView: childView)
//3.继续递归
let firstView = childView.hitTest(childPoint, withEvent: event)
//4.发现不为空,就回传
if let notEmpty = firstView {
return notEmpty
}
}
//5.如果没知道合适的处理事件控件,就返回自己
return self
}
- 上面说的自己写
self.convertPoint(point, toView: childView)
这方法,其实就是下面的算法
func myConvertPoint(point:CGPoint, toView view:UIView) -> CGPoint {
//1.拿到子控件的x,y
let x = view.frame.origin.x
let y = view.frame.origin.y
//2.进行减法,并且返回
return CGPointMake(point.x - x, point.y - y)
}
Objective-C
- 这里我的另一种思路,但是不提倡
/*只要进入了这个方法,那就代表了这个视图,是可以与用户交互,包含那个点的。
但是站在面向对象的角度,思考问题,”谁最清楚,控件能否被点击,
点在不在自己身上,当然是自己",所以不提倡这样的写法*/
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
int count = (int)self.subviews.count;
for( int i = count - 1 ; i >= 0 ; --i ) {
//1.拿到子控件
UIView * childView = self.subviews[i];
//2.转换坐标点
CGPoint childPoint = [self convertPoint:point toView:childView];
//3.判断子控件能不能与用户交互
BOOL flagEnable = childView.userInteractionEnabled == YES || childView.hidden == NO || childView.alpha > 0.01;
//4.判断点在不在子控件上
BOOL flagInside = [childView pointInside:childPoint withEvent:event];
//5.如果都可以,那么继续递归
if (flagInside && flagEnable) {
UIView *firstView = [childView hitTest:childPoint withEvent:event];
//6.如果有,就回传
if (firstView) {
return firstView;
}
}
}
//7.没找到最合适的就返回自己
return self;
}
网友评论