时间:2021-05-19
hitTest:withEvet 调用过程
比如如果是当前的View A, 还有一个viewB
如果不重写 hitTest 方法,那么 系统默认是先调用viewA的hitest 方法,然后再调用viewB的htest方法。
系统的调用过程,跟下面的重写hitest的方法是一模一样的。
复制代码 代码如下:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if ([self pointInside:point withEvent:event]) {
}
else {
return nil;
}
for (UIView *subView in self.subviews) {
if ([subView hitTest:point withEvent:event]!=nil) {
return subView;
}
}
return self;
}
在说明一次,如果不重写hitest方法,那么每一个UIVIeew的默认hitest的方法都是上面这个处理流程。
那也没啥好说的。
但是对于一些特殊的处理过程,就不行了
所以之所以重写hitTest方法,通常都是为了穿透上层 的 UIview,让touch事件可以达到下面的uiview,
比如 view A 和 VIew B,
View b完全挡住了view A,但是我想让点击viewB的时候,view A可以响应点击的事件。就可以采用下面的方法:
复制代码 代码如下:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if ([self pointInside:point withEvent:event]) {
NSLog(@"in view A");
return self;
}
else {
return nil;
}
}
深入
我们来更深入一下,现在有个实例需求界面如下,
Window
-ViewA
-ButtonA
-ViewB
-ButtonB
层次结构:ViewB完全盖住了ButtonA,ButtonB在ViewB上,现在需要实现:
(1)ButtonA和ButtonB都能响应消息 (2)ViewA也能收到ViewB所收到的touches消息 (3)不让ViewB(ButtonB)收到消息。
(首先解析下,默认情况下,点击了ButtonB的区域,iOS消息处理过程。
-ViewA
-ButtonA
-ViewB
-ButtonB
当点击ButtonB区域后,处理过程:从ViewA开始依次调用hitTest
pointInside的值依次为:
ViewA:NO;
ViewB:YES;
ButtonB:YES;
ButtonB的subViews:NO;
所以ButtonB的subViews的hitTest都返回nil,于是返回的处理对象是ButtonB自己。接下去开始处理touches系列方法,这里是调用ButtonB绑定的方法。处理完后消息就停止,整个过程结束。)
分析:
实现的方式多种,这里将两个需求拆解开来实现,因为实现2就可以满足1。
需求1的实现,ViewB盖住了ButtonA,所以默认情况下ButtonA收不到消息,但是在消息机制里寻找消息响应是从父View开始,所以我们可以在ViewA的hitTest方法里做判断,若touch point是在ButtonA上,则将ButtonA作为消息处理对象返回。
代码如下:
复制代码 代码如下:
#pragma mark - hitTest
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// 当touch point是在_btn上,则hitTest返回_btn
CGPoint btnPointInA = [_btn convertPoint:point fromView:self];
if ([_btn pointInside:btnPointInA withEvent:event]) {
return _btn;
}
// 否则,返回默认处理
return [super hitTest:point withEvent:event];
}
这样,当触碰点是在ButtonA上时,则touch消息就被拦截在ViewA上,ViewB就收不到了。然后ButtonA就收到touch消息,会触发onClick方法。
需求2的实现,上面说到响应链,ViewB只要override掉touches系列的方法,然后在自己处理完后,将消息传递给下一个响应者(即父View即ViewA)。
代码如下:在ViewB代码里
复制代码 代码如下:
#pragma mark - touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"B - touchesBeagan..");
// 把事件传递下去给父View或包含他的ViewController
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"B - touchesCancelled..");
// 把事件传递下去给父View或包含他的ViewController
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"B - touchesEnded..");
// 把事件传递下去给父View或包含他的ViewController
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"B - touchesMoved..");
// 把事件传递下去给父View或包含他的ViewController
[self.nextResponder touchesBegan:touches withEvent:event];
}
然后,在ViewA上就可以接收到touches消息,在ViewA上写:
复制代码 代码如下:
#pragma mark - touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"A - touchesBeagan..");
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"A - touchesCancelled..");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"A - touchesEnded..");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"A - touchesMoved..");
}
这样就实现了向父View透传消息。
不让ViewB收到消息,可以设置ViewB.UserInteractionEnable=NO;除了这样还可以override掉ViewB的ponitInside,原理参考上面。
在ViewB上写:
复制代码 代码如下:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// 本View不响应用户事件
return NO;
}
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
UIEvent是一系列UITouch的集合,在IOS中负责响应触摸事件。另外还提到了响应者链的概念,在IOS中,所有事件有一个最先响应者,事件可以沿着响应者链向
android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。一个最简单的屏幕触摸动作触发了一系列To
IOS中多手势之间的冲突和解决办法UIImageView默认是不支持交互的,也就是userInteractionEnabled=NO,因此要接收触摸事件(手势识
在ios端,safari浏览器上触发click事件有300ms的延迟相应,为touch添加的样式会和click冲突而出现闪烁问题在safari中触摸事件的相应顺
前言上一篇讲了Android触摸事件的传递机制,具体可以看这里初识Android触摸事件传递机制。既然知道Android中触摸事件的传递分发,那么它能解决什么样