Как правильно отслеживать касания между касаниями Beg: и touchesEnded :? - PullRequest
9 голосов
/ 09 сентября 2011

При некотором обслуживании кода мультитач «рисование на экране» я обнаружил ошибку, связанную с тем, как ссылки на экземпляры касаний должны устанавливаться между касаниями.1002 * Код использует экземпляр NSDictionary для хранения ссылок касаний в touchesBegan: withEvent :, следующим образом:

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

И извлекает их в касанияхEnded: 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 иногда оказывается равным нулю, поскольку ключ не известен в словаре.Иногда я имею в виду около 1% времени.

Теперь контекст задан, мои вопросы:

  1. Есть идеи, почему этот код глючит?Я не уверен, почему это не работает, особенно из-за очень низкой частоты ошибок

  2. Некоторые документы от Apple утверждают, что это хорошая идея для использованияадреса объектов UITouch в виде ключей : но это означает использование объектов NSValue * вместо объектов NSString * ( и, следовательно, использование CFDictionaryRef вместо NSDictionary, поскольку UITouch не реализует протокол копирования). Почему хранение представления адреса NSString, а не самого NSValue, не может быть простым решением для использования NSDictionary? Обновление: Apple обновила страницу и удалила пример храненияUITouch через NSString и просто упоминает решение CFDictionaryRef, не являющееся объектом вообщепонять причину ошибки в этом коде.

Ответы [ 3 ]

4 голосов
/ 19 ноября 2015

Вы можете использовать свойство hash из UITouch для ObjC и Swift. Это одно и то же касание для нескольких событий (вверх, вниз, перемещение и т. Д.). Это Int, но вы можете преобразовать его в String

4 голосов
/ 05 октября 2011

Когда вы используете stringWithFormat для преобразования экземпляра NSValue в строку, я полагаю, что вы, по сути, вызываете [описание touchValue], которое, я не уверен, гарантирует согласованный результат. Например, что если в описание включен указатель на сам экземпляр 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];
}

На самом деле нет причин ограничивать себя строковыми ключами, если только вам не нужно использовать кодирование значения ключа или сериализовать словарь в список свойств.

0 голосов
/ 06 октября 2011

Не использовать

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

для ключа. Наилучшим способом было бы установить значение сенсорного указателя в качестве ключа

id key = touch;

или, если вы предпочитаете строки, используйте этот случай:

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