Как программно имитировать жест смахивания?

В настоящее время я пытаюсь написать несколько приемочных тестов для нашего нового приложения iOS, используя frank (и, в свою очередь, UISpec). Хотя фреймворк поддерживает прикосновения как основной способ взаимодействия с представлениями, в настоящее время он не поддерживает более сложные жесты (например, сжатие, пролистывание и т. д.). Мне нужно добавить поддержку смахивания хотя бы потому, что это ядро ​​функциональности нашего приложения, и без него наши тесты будут практически бесполезны.

Реализация этого должна быть довольно простой, если я смогу найти способ смоделировать события в Cocoa. Можно отправлять жесты смахивания, если вы используете платформу Apple UIAutomation (см. здесь), так что есть пример чего-то, что генерирует эти события извне. Я поискал в Интернете, но не нашел примеров того, как это делают люди (хотя был тема, в которой кто-то ранее спрашивал о чем-то подобном...).

Заранее большое спасибо за помощь/идеи...


person jkp    schedule 15.02.2011    source источник


Ответы (1)


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

Во всяком случае, для тех, кто пытается сделать что-то подобное. Я основывал свое решение на API, подробно описанном в этом сообщении. — я записал последовательность событий, которую хотел смоделировать, а затем воспроизвел их. Единственная загвоздка в том, что я не мог заставить работать встроенный API воспроизведения (у меня был тот же сбой, упомянутый в комментариях, упомянутых внизу). Покопавшись некоторое время в мире ASM, я написал свою собственную версию.

@implementation UIApplication (EventReplay)

///
/// - replayEventsFromFile:
///
- (void)replayEventsFromFile:(NSString *)filename 
{
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
  NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:filename];
  NSArray* eventList = [[NSArray arrayWithContentsOfFile:filePath] retain];
  [self replayEvents:eventList];
}

///
/// - replayEvents:
///
- (void)replayEvents:(NSArray *)events
{
  if (!events.count)
    return;

  NSDictionary *eventDict = [events objectAtIndex:0U];
  GSEventRef thisEvent = GSEventCreateWithPlist((CFDictionaryRef)eventDict);

  uint64_t eventTime = thisEvent->record.timestamp;
  thisEvent->record.timestamp = mach_absolute_time();

  mach_port_t appPort = GSCopyPurpleNamedPort([[[NSBundle mainBundle] bundleIdentifier] UTF8String]);
  GSSendEvent(&thisEvent->record, appPort);
  mach_port_deallocate(mach_task_self(), appPort); 

  if (events.count <= 1)
    return;

  NSIndexSet *remainderIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, events.count - 1)];
  NSArray *remainingEvents = [events objectsAtIndexes:remainderIndexes];

  GSEventRef nextEvent = GSEventCreateWithPlist((CFDictionaryRef)[remainingEvents objectAtIndex:0U]);
  NSTimeInterval nextEventDelay = GetTimeDelta(nextEvent->record.timestamp, eventTime);

  if (nextEventDelay > 0.05)
    [self performSelector:@selector(replayEvents:) withObject:remainingEvents afterDelay:nextEventDelay];
  else
    [self replayEvents:remainingEvents];

  CFRelease(nextEvent);
  CFRelease(thisEvent);
}

@end

Фрагмент выше показывает, как я воспроизводю события. Моя реализация довольно грубая - вы увидите, что мне пришлось выдумывать тот факт, что если я вслепую использую таймер для планирования следующего события, иногда он не срабатывает - кажется, когда задержка слишком мала. Ужасный взлом, который вы видите, кажется, заставляет все работать нормально

В любом случае, надеюсь, что это поможет кому-то еще в некотором роде.

person jkp    schedule 16.02.2011
comment
Работает ли этот метод с iOS 5? Как включить GSEventRef? - person Anthony Blake; 11.12.2011