【问题标题】:A count of started touches is not equal to count of finished touches开始触摸的计数不等于完成触摸的计数
【发布时间】:2014-04-16 09:52:16
【问题描述】:

我有以下代码用于测试目的:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self customTouchHandler:touches];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self customTouchHandler:touches];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self customTouchHandler:touches];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self customTouchHandler:touches];
}
- (void)customTouchHandler:(NSSet *)touches
{
    for(UITouch* touch in touches){
        if(touch.phase == UITouchPhaseBegan)
            touchesStarted++;
        if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
            touchesFinished++;
    }
    NSLog(@"%d / %d", touchesStarted, touchesFinished);
}

我想当屏幕上没有触摸时,touchesStarted 应该总是等于touchesFinished,但是我有一个非常奇怪的输出:

2014-04-16 13:44:27.780 App[5925:60b] 2 / 0
2014-04-16 13:44:27.911 App[5925:60b] 2 / 1

我用两根手指按下屏幕,然后几乎(但不是)同时松开。

我错过了什么吗?为我的视图启用了多次触摸。对了,视图是SKView,代码属于我自定义的SKScene

更新

由于你们中的许多人无法重现这种奇怪的行为,我准备了一个示例 Xcode 项目:https://www.dropbox.com/s/qmgxka1gtgwquio/TapTest.zip

尝试用两根手指同时敲击多次。 touchesStarted 必须等于 touchesEnded 移除手指时,对吧?但他们不是。这是我的输出:

2014-04-24 12:49:06.359 TapTest[8207:60b] 1 / 0
2014-04-24 12:49:06.376 TapTest[8207:60b] 2 / 0
2014-04-24 12:49:06.458 TapTest[8207:60b] 2 / 0
2014-04-24 12:49:06.460 TapTest[8207:60b] 2 / 1
2014-04-24 12:49:06.491 TapTest[8207:60b] 2 / 2
2014-04-24 12:49:07.325 TapTest[8207:60b] 3 / 2
2014-04-24 12:49:07.342 TapTest[8207:60b] 4 / 2
2014-04-24 12:49:07.408 TapTest[8207:60b] 4 / 2
2014-04-24 12:49:07.410 TapTest[8207:60b] 4 / 3
2014-04-24 12:49:07.426 TapTest[8207:60b] 4 / 3
2014-04-24 12:49:07.441 TapTest[8207:60b] 4 / 4
2014-04-24 12:49:07.842 TapTest[8207:60b] 6 / 4
2014-04-24 12:49:07.925 TapTest[8207:60b] 6 / 4
2014-04-24 12:49:07.941 TapTest[8207:60b] 6 / 5
2014-04-24 12:49:08.042 TapTest[8207:60b] 8 / 5
2014-04-24 12:49:08.125 TapTest[8207:60b] 8 / 6
2014-04-24 12:49:08.259 TapTest[8207:60b] 9 / 6
2014-04-24 12:49:08.293 TapTest[8207:60b] 9 / 6
2014-04-24 12:49:08.308 TapTest[8207:60b] 9 / 7
2014-04-24 12:49:08.425 TapTest[8207:60b] 10 / 7
2014-04-24 12:49:08.442 TapTest[8207:60b] 11 / 7
2014-04-24 12:49:08.444 TapTest[8207:60b] 11 / 7
2014-04-24 12:49:08.492 TapTest[8207:60b] 11 / 8
2014-04-24 12:49:08.575 TapTest[8207:60b] 11 / 9
2014-04-24 12:49:08.642 TapTest[8207:60b] 12 / 9
2014-04-24 12:49:08.659 TapTest[8207:60b] 13 / 9
2014-04-24 12:49:08.660 TapTest[8207:60b] 13 / 9
2014-04-24 12:49:08.692 TapTest[8207:60b] 13 / 9
2014-04-24 12:49:08.694 TapTest[8207:60b] 13 / 10
2014-04-24 12:49:08.708 TapTest[8207:60b] 13 / 10
2014-04-24 12:49:08.741 TapTest[8207:60b] 13 / 11
2014-04-24 12:49:08.792 TapTest[8207:60b] 14 / 11
2014-04-24 12:49:08.809 TapTest[8207:60b] 15 / 11
2014-04-24 12:49:08.810 TapTest[8207:60b] 15 / 11
2014-04-24 12:49:08.890 TapTest[8207:60b] 15 / 11
2014-04-24 12:49:08.892 TapTest[8207:60b] 15 / 12
2014-04-24 12:49:08.908 TapTest[8207:60b] 15 / 13
2014-04-24 12:49:09.042 TapTest[8207:60b] 17 / 13
2014-04-24 12:49:09.141 TapTest[8207:60b] 17 / 14
2014-04-24 12:49:09.242 TapTest[8207:60b] 19 / 14
2014-04-24 12:49:09.341 TapTest[8207:60b] 19 / 14
2014-04-24 12:49:09.358 TapTest[8207:60b] 19 / 15
2014-04-24 12:49:09.441 TapTest[8207:60b] 21 / 15
2014-04-24 12:49:09.525 TapTest[8207:60b] 21 / 15
2014-04-24 12:49:09.542 TapTest[8207:60b] 21 / 15
2014-04-24 12:49:09.559 TapTest[8207:60b] 21 / 16
2014-04-24 12:49:09.608 TapTest[8207:60b] 22 / 16
2014-04-24 12:49:09.625 TapTest[8207:60b] 23 / 16
2014-04-24 12:49:09.626 TapTest[8207:60b] 23 / 16
2014-04-24 12:49:09.708 TapTest[8207:60b] 23 / 16
2014-04-24 12:49:09.709 TapTest[8207:60b] 23 / 17
2014-04-24 12:49:09.774 TapTest[8207:60b] 23 / 18
2014-04-24 12:49:09.810 TapTest[8207:60b] 24 / 18
2014-04-24 12:49:09.826 TapTest[8207:60b] 25 / 18
2014-04-24 12:49:09.828 TapTest[8207:60b] 25 / 18
2014-04-24 12:49:09.908 TapTest[8207:60b] 25 / 18
2014-04-24 12:49:09.909 TapTest[8207:60b] 25 / 19
2014-04-24 12:49:09.974 TapTest[8207:60b] 25 / 20
2014-04-24 12:49:09.992 TapTest[8207:60b] 26 / 20
2014-04-24 12:49:10.026 TapTest[8207:60b] 27 / 20
2014-04-24 12:49:10.027 TapTest[8207:60b] 27 / 20
2014-04-24 12:49:10.091 TapTest[8207:60b] 27 / 20
2014-04-24 12:49:10.094 TapTest[8207:60b] 27 / 21
2014-04-24 12:49:10.125 TapTest[8207:60b] 27 / 22

【问题讨论】:

  • 为什么要调用 touchesMoved 上的方法。只检查 touchesBegan 和 touchesEnded
  • @SunnyShah 它对手头的问题没有影响。正如方法名称所述,customTouchHandler 稍后可用于处理一些可能使用或不使用已移动触摸的自定义触摸。
  • @SunnyShah 没关系。如果我删除 touchesMoved,结果将是相同的
  • yaaa 我显示 if 条件
  • @AndreyGordeev 也无法复制。你能分享一下 touchesStarted 和 touchesFinished 的初始化代码吗?可能是您使用的是原始类型,而 64 位正在干扰您。您可以尝试将日志记录为 NSNumber 对象,以确保:NSLog(@"%@ / %@", @(touchesStarted), @(touchesFinished)); - 和/或确保您正在初始化 NSIntegers(不仅仅是 int)等。远射但如果没有其他人可以重现,这可能是您的问题正在做我们看不到的事情。

标签: ios objective-c sprite-kit uitouch


【解决方案1】:

也有这个问题。

目前我发现的唯一解决方案是基于观察触摸阶段:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"Began %lu of %lu", [touches count], [event.allTouches count]);

    for (UITouch *touch in touches) {
        [touch addObserver:self forKeyPath:@"phase" options:0 context:nil];
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"Ended %lu of %lu", [touches count], [event.allTouches count]);
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    if ([object isKindOfClass:[UITouch class]]) {
        UITouch *touch = object;
        NSLog(@"Touch %lu phase: %ld", (unsigned long)[touch hash], [touch phase]);
        if (touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled) {
            NSLog(@"Touch %lu ended", (unsigned long)[touch hash]);
            [touch removeObserver:self forKeyPath:@"phase"];
        }
    }
}

【讨论】:

    【解决方案2】:

    这似乎是 SKView 的 touchesBegan: 方法中的一个错误。据我所知,Apple 正在使用字典将每个 UITouch 与应该将触摸方法转发到的对象相关联。从我的测试看来,如果有多个触摸同时开始,那么只有一个触摸会被添加到字典中。因此,您的场景将永远不会收到其他触摸的与触摸相关的方法调用,这就是为什么您会看到 touchesStarted 和 touchesFinished 之间的差异。

    如果您将 SKView 子类化并覆盖 touchesBegan: 方法,您的场景应该会得到正确的调用。

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        for (UITouch *touch in touches) {
            NSSet *newSet = [NSSet setWithObject:touch];
            [super touchesBegan:newSet withEvent:event];
        }
    }
    

    这解决了我的问题,但是多次调用 super 方法可能会产生一些副作用。

    【讨论】:

      【解决方案3】:

      无法评论,所以在这里回答。您可以像这样打印所有处理程序,也许它会有所帮助:

      - (void)customTouchHandler:(NSSet *)touches
      {
          static int handlerCounter = 0;
          handlerCounter++;
          for(UITouch* touch in touches){
              NSLog(@"%d: %d", handlerCounter, touch.phase);
              if(touch.phase == UITouchPhaseBegan)
                  touchesStarted++;
              if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
                  touchesFinished++;
          }
          NSLog(@"%d / %d", touchesStarted, touchesFinished);
      }
      

      是否可以在 obj-c 中切换(touch.phase)?如果评估正确,第二个中的“或”是否,请尝试使用括号。

      【讨论】:

      • 同样的结果:59 / 55。感谢回复
      【解决方案4】:

      如果您使用多个手指,则应在您的init 中包含self.view.multipleTouchEnabled = YES;

      我对您提供的代码进行了一些广泛的测试,唯一一次数字不匹配是当一个或两个手指的组合非常快速地进行录音时。在这种情况下,iOS 会丢失联系人当前状态的跟踪。

      这与一个精灵在另一个精灵上快速上下弹跳非常相似,这有时会导致 didBeginContact 中的错误接触状态。

      目前您无法通过多次触摸来解决此问题。如果您使用单次触摸,您的计数将始终(至少在我的测试中)匹配。

      【讨论】:

      • 如果我设置self.view.multipleTouchEnabled = YES结果是一样的,我也试过了。
      • "在这种情况下,iOS 会丢失联系人当前状态的跟踪。" - 似乎是这样。我的问题是如何避免这种情况? :)
      • 另外,AppStore 中至少有一款游戏在触摸处理时没有这个问题——Moon Lander
      【解决方案5】:

      我之前也遇到过同样的问题 (touchesEnded:withEvent: is not called)。

      我的测试表明,touchEnded 没有被调用 100% 的时间。我的解决方案:将四个触摸处理方法放在 AppDelegate.m 文件中,并将触摸转发到您的视图控制器。我不知道为什么会这样,但这样做后我没有任何问题。

      【讨论】:

        猜你喜欢
        • 2011-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多