【问题标题】:How to track touches in a consistent way between touchesBegan: and touchesEnded:?如何在 touchesBegan: 和 touchesEnded: 之间以一致的方式跟踪触摸?
【发布时间】:2011-09-09 10:11:41
【问题描述】:

在一些多点触控“在屏幕上绘图”代码的维护中,我遇到了一个关于如何在 touchesBegan:withEvent:、touchesMoved:withEvent: 和 touchesEnded:withEvent: 之间设置对触控实例的引用的错误。

代码使用一个NSDictionary实例在touchesBegan:withEvent:中存储touches的引用,如下:

for (UITouch *touch in touches]) {
    NSValue *touchValue = [NSValue valueWithPointer:touch];
    NSString *key = [NSString stringWithFormat:@"%@", touchValue];
    [myTouches setValue:squiggle forKey:key];
}

并在 touchesEnded:withEvent: 中检索它们:

for (UITouch *touch in touches) {
    NSValue *touchValue = [NSValue valueWithPointer:touch];
    NSString *key = [NSString stringWithFormat:@"%@", touchValue];
    NSObject *valueFromDictionary = [myTouches valueForKey:key];
    [myTouches removeObjectForKey:key];
}

在最后一段代码中,valueFromDictionary 碰巧偶尔为 nil,因为字典不知道键。偶尔,我的意思是大约 1% 的时间。

现在上下文已经设置好了,我的问题是:

  1. 知道为什么这段代码有问题吗?我不确定为什么它不起作用,尤其是因为错误频率非常低

  2. Some doc from Apple 表示使用 UITouch 对象的地址作为键是一个好主意 :但这意味着使用 NSValue* 对象而不是 NSString* 对象( 并因此使用 CFDictionaryRef 而不是NSDictionary 因为 UITouch 没有实现复制协议)。 为什么存储地址的 NSString 表示而不是 NSValue 本身不能成为能够使用 NSDictionary 的简单解决方案? 更新:Apple 已更新页面并删除了存储示例通过 NSString 的 UITouch 并仅提及 not-object-at-all CFDictionaryRef 解决方案。

恐怕我离 C 和指针的东西还很远,需要帮助来理解这段代码中存在错误的原因。

【问题讨论】:

    标签: ios pointers reference multi-touch touchesbegan


    【解决方案1】:

    您可以将 hashUITouch 属性用于 ObjC 和 Swift。跨多个事件(向上、向下、移动等)的相同触摸是相同的。这是一个Int,但您可以将其转换为String

    【讨论】:

      【解决方案2】:

      当您使用 stringWithFormat 将 NSValue 实例转换为字符串时,我相信您实际上是在调用 [touchValue description],我不确定这是否能保证一致的结果。例如,如果描述包括一个指向 NSValue 实例本身的指针,就像许多对象一样?在这种情况下,存储完全相同的指针的两个不同的 NSValue 实例会产生不同的字符串键。

      我很想看看当您运行以下代码时会发生什么:

      for (UITouch *touch in touches) {
        NSValue *touchValue = [NSValue valueWithPointer:touch];
        NSString *key = [NSString stringWithFormat:@"%@", touchValue];
        NSObject *valueFromDictionary = [myTouches valueForKey:key];
      
        if (valueFromDictionary == nil) {
          NSLog(@"%@", [myTouches allKeys]);
          NSLog(@"%@", key);
        }
      
        [myTouches removeObjectForKey:key];
      }
      

      这将准确地告诉我们密钥发生了什么变化。但是,如果您只想解决您遇到的问题,我敢打赌,如果您直接使用 NSValue 对象作为键,它会起作用,因为它确实符合 NSCopying 协议。本质上,指针 (UITouch *) 只是一个数字,用于唯一标识 UITouch 实例在内存中的存储位置。 NSValue 封装这个数字的方式与 NSNumber 非常相似,因此您可以将其视为数组中的数字键。只要触摸继续占据内存中的相同位置(正如 Apple 建议的那样),它应该可以完美地作为键工作,并且您的代码会更简单:

      (touchesBegan:withEvent:)

      for (UITouch *touch in touches]) {
        NSValue *key = [NSValue valueWithPointer:touch];
        [myTouches setValue:squiggle forKey:key];
      }
      

      (touchesEnded:withEvent:)

      for (UITouch *touch in touches) {
        NSValue *key = [NSValue valueWithPointer:touch];
        NSObject *valueFromDictionary = [myTouches valueForKey:key];
        [myTouches removeObjectForKey:key];
      }
      

      确实没有理由将自己局限于字符串键,除非您需要使用键值编码或将字典序列化为属性列表。

      【讨论】:

      • 我尝试了 NSValue 解决方案,但结果与 NSString 相同:99% 的时间都可以正常工作,但仍有 1% 的错误。现在我假设它要么是来自 Apple 的错误(事实上,我在问题中提到的页面几天前刚刚更新,这是另一个线索,即使 Apple 员工也不确定这个问题),或者是时间之间的并发问题我把数据放进去,把数据拿出来。
      【解决方案3】:

      这对我有用:

      -(NSString * ) keyForTouch:(UITouch *) touch {
          return [NSString stringWithFormat:@"%lu", (unsigned long)[touch hash]];
      }
      
      //Example method, how to use:
      
      -(UIView *) getViewForTouch:(UITouch*) touch{
          NSString * key = [self keyForTouch:touch];
          return [self.myDictionary objectForKey:key];
      }
      

      【讨论】:

        【解决方案4】:

        不要使用

        NSString *key = [NSString stringWithFormat:@"%@", touchValue];
        

        钥匙。最好的方法是将触摸指针值设置为键

        id key = touch;
        

        或者如果您更喜欢字符串,请使用这种情况:

        NSString *key = [NSString stringWithFormat:@"%x", touch]; // %x prints hex value of touch
        

        【讨论】:

        • %x 解决方案不起作用:您存储的是指针的值而不是指向的地址(我在测试后实际上意识到了这一点:错误出现 100%时间而不是 1%)
        • 看起来很奇怪。你可以在你的'touchesBegan'和'touchesEnded'方法中设置断点并比较'UITouch *touch'的地址(并将其与通过@“x”保存到键的值进行比较)吗?据我所知,所有这些值必须相等。我在我的应用程序中使用相同的方法并且它有效。另请检查您是否将 'touch' 而不是 'touchValue' 传递给 @"x"。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-12
        相关资源
        最近更新 更多