UIView的-hitTest:withEvent:调用了三次?

发布于 2024-10-04 13:33:49 字数 2087 浏览 3 评论 0原文

我试图拦截整个应用程序内发生的任何活动(即触摸)。

换句话说,我试图收到主 UIView(包含其余控件)中发生的任何触摸事件的通知。 为此,我认为 UIView 的方法 -hitTest:withEvent: 是一个很好的解决方案。

但是,当我在调用 [super hitTest:... withEvent:...] 之前 NSLog 进入此重写方法时,我发现对于我所做的任何触摸,它都会被调用 3 次,并且我看不到收到的事件有任何差异每次被调用时。

以下是如何在我的应用程序的主视图中实现该方法:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    NSLog(@"hitTest:withEvent called :");
    NSLog(@"Event: %@", event);
    NSLog(@"Point: %@", NSStringFromCGPoint(point));
    NSLog(@"Event Type: %d", event.type);
    NSLog(@"Event SubType: %d", event.subtype);
    NSLog(@"---");

    return [super hitTest:point withEvent:event];
}

这是我在此视图中单次触摸的 NSLog:

2010-11-29 14:09:26.892 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.892 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.892 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.892 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.892 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.893 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.893 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.893 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.893 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.894 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37944.9 touches: {(
)}
2010-11-29 14:09:26.894 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.894 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.894 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.894 Application[68818:207] ---

我如何在这三个通知之间做出任何区别,以便触发我只想执行的操作单次触摸一次?

提前致谢 !

I am trying to intercept any activity (i.e. touches) that happens inside my whole application.

In other words, I am trying to be notified of any touch event that happens within my main UIView, containing the rest of my controls.
To do so, I thought the UIView's method -hitTest:withEvent: was a good solution.

However, when I NSLog into this overriden method before calling [super hitTest:... withEvent:...], I see that it is called 3 times for any touch I make, and I cannot see any difference in the event I receive each time it is called.

Here is how is implemented the method in the main view of my application :

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    NSLog(@"hitTest:withEvent called :");
    NSLog(@"Event: %@", event);
    NSLog(@"Point: %@", NSStringFromCGPoint(point));
    NSLog(@"Event Type: %d", event.type);
    NSLog(@"Event SubType: %d", event.subtype);
    NSLog(@"---");

    return [super hitTest:point withEvent:event];
}

And here is what I NSLog for a single touch in this view :

2010-11-29 14:09:26.892 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.892 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.892 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.892 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.892 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.893 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.893 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.893 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.893 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.894 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37944.9 touches: {(
)}
2010-11-29 14:09:26.894 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.894 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.894 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.894 Application[68818:207] ---

How could I make any difference between those three notifications in order to trigger the action I want to make only one time for a single touch ?

Thanks in advance !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

风追烟花雨 2024-10-11 13:33:49

确实有 3 次调用 hitTest。目前尚不清楚原因,但我们可以通过事件的时间戳推测前两个调用与完成前一个手势有关 - 这些时间戳总是非常接近上一次触摸发生的时间,并且与上次触摸发生的时间有一定距离。当前时间。

这就是我确定要处理哪个 hitTest 的方法:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  NSTimeInterval system = [[NSProcessInfo processInfo] systemUptime];

  if (system - event.timestamp > 0.1) {
    // not the event we were interested in
  } else {
    // use this call
  }
}

There are indeed 3 calls to hitTest. It is not clear why, but we can surmise by the timestamps on the event that the first two calls are to do with completing the previous gesture - those timestamps are always very close to whenever the previous touch happened, and will be some distance from the current time.

This is how I determine which hitTest to process:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  NSTimeInterval system = [[NSProcessInfo processInfo] systemUptime];

  if (system - event.timestamp > 0.1) {
    // not the event we were interested in
  } else {
    // use this call
  }
}
小草泠泠 2024-10-11 13:33:49

如果这对您来说仍然是个问题。

我找到了有关该主题的几个示例和讨论,但正确的解决方案非常简单。

一般来说,hittest 在 UIView 或 UIScrollView 中被调用三次 - 这是遍历视图层次结构的结果。

一个非常简单且对我来说始终合适的解决方案是:

在您为我实现的视图中实现函数 hittest

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

- 所有三个函数调用始终具有相同的位置 - 因此只需将位置存储在本地类中。

在 .h 文件中:

@interface MyScrollView : UIScrollView {
    CGPoint touchLocation_;
}

@property (nonatomic, readonly) CGPoint touchLocation;

在 .m 文件中

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    return [super hitTest:point withEvent:event];
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
// The point check you need    
    if(point.y != self.touchLocation.y){
        touchLocation_ = point;
    // The function call you need - just called once.
    }
    return [super pointInside:point withEvent:event];
}

该解决方案在几个项目中对我来说效果很好。

If this is still a problem for you.

I found several examples and discussions about this topic but the proper solution is quite simple.

In general hittest is called three times within a UIView or a UIScrollView - this results of traversing the view-hierarchy.

A quite simple and for me always suitable solution is:

implement the function hittest in the view you implemented

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

for me - all three function calls have always the same position - so just store the location in your local class.

in the .h File:

@interface MyScrollView : UIScrollView {
    CGPoint touchLocation_;
}

@property (nonatomic, readonly) CGPoint touchLocation;

in the .m File

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    return [super hitTest:point withEvent:event];
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
// The point check you need    
    if(point.y != self.touchLocation.y){
        touchLocation_ = point;
    // The function call you need - just called once.
    }
    return [super pointInside:point withEvent:event];
}

This solution works for me in several projects quite well.

孤君无依 2024-10-11 13:33:49

您收到的事件响应的数量取决于视图层次结构。

该方法遍历视图
通过发送层次结构
pointInside:withEvent: 给每个人的消息
子视图来确定哪个子视图
应该收到触摸事件。如果
pointInside:withEvent: 返回 YES,
那么子视图的层次结构是
穿越;否则,其分支
视图层次结构被忽略。你
很少需要调用这个方法
你自己,但你可以覆盖它
从子视图中隐藏触摸事件。

此方法会忽略以下视图对象
被隐藏,已禁用用户
相互作用,或具有α水平
小于0.01。该方法不
考虑视图的内容
确定命中时。于是,一个观点
即使
指定点位于透明区域
该视图内容的一部分。

位于接收者范围之外的点
边界永远不会被报告为命中,
即使它们实际上位于一处
接收者的子视图。这个可以
如果当前视图的
ClipsToBounds 属性设置为 NO
并且受影响的子视图扩展
超出视图范围。

来自 UIView 类参考

简而言之,如果您触摸的视图具有三个子视图,并且这些视图可见并且在其父视图和触摸区域的范围内,您将收到三个命中测试响应。

The number of event responses you receive depends on the view hierarchy.

This method traverses the view
hierarchy by sending the
pointInside:withEvent: message to each
subview to determine which subview
should receive a touch event. If
pointInside:withEvent: returns YES,
then the subview’s hierarchy is
traversed; otherwise, its branch of
the view hierarchy is ignored. You
rarely need to call this method
yourself, but you might override it to
hide touch events from subviews.

This method ignores view objects that
are hidden, that have disabled user
interaction, or have an alpha level
less than 0.01. This method does not
take the view’s content into account
when determining a hit. Thus, a view
can still be returned even if the
specified point is in a transparent
portion of that view’s content.

Points that lie outside the receiver’s
bounds are never reported as hits,
even if they actually lie within one
of the receiver’s subviews. This can
occur if the current view’s
clipsToBounds property is set to NO
and the affected subview extends
beyond the view’s bounds.

From the UIView Class Reference.

In a nutshell, if the view you touch has three subviews, and those views are visible and within the bounds of their superview and touch region, you will receive three hit test responses.

匿名的好友 2024-10-11 13:33:49

这个问题已经被问了好几年了,但我想回答一下:

系统多次调用 hitTest 和 pointInside 来固定位置,每次调用堆栈都不同,请参考 苹果官方回复

This question has been asked for years, but I want to answer it:

The system calls hitTest and pointInside several times to fix the position, and each time the call stack is different, please refer to the official reply from apple.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文