2

我正在开发一个在应用程序的主线程中运行的 UI 测试框架(KIF-next)。基本流程是:

  1. 执行一些测试逻辑。
  2. 通过 旋转主循环 0.1 秒runUntilDate:
  3. 重复。

这种方法非常有效。您可以模拟点击事件,您可以在滑块上滑动,观看警报视图动画进出,scrollToRowAtIndexPath:。除了一个特定的场景,拖动滚动视图,一切都很好。

您可以上下拖动滚动视图,但是当您松开手指时,什么也没有发生。此外,其他视图不再识别触摸事件,您所能做的就是拖动滚动视图。

如果此执行模式结束并且代码退出,滚动视图将执行它们的反弹/减速,但这不是一个选项,因为我在执行顺序执行的 ocunit 中工作。

我的问题:为什么会发生这种情况以及可以做些什么来纠正它?

我有两个演示:

演示 1:阻止您的整个应用程序

使用此代码,您可以测试您的应用程序并确认一切正常,直到您滚动。在那之后,没有任何效果。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        while (YES) {
            [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
        }
    });
    return YES;
}

Demo 2:暂时阻塞

如果将此代码连接到一个按钮,如果您先点击按钮然后滚动,您将看到与上述相同的效果。一旦计时器完成,您将看到您的应用程序恢复生机。

- (IBAction)spinButtonClicked:(id)sender
{
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
}
4

1 回答 1

8

正如 Dave 所指出的,问题与运行循环模式有关。应用程序将主要驻留在该kCFRunLoopDefaultMode模式下,但在用户开始滚动时切换到UITrackingRunLoopMode,当滚动视图完成减速时再次返回。这似乎是为了防止普通计时器在滚动期间触发。(您可以在 Safari 中看到这一点,其中动画在滚动期间停止更新。)

机制似乎如下。

  1. UIApplication一个堆栈来跟踪应用程序请求的当前运行循环模式。
  2. 当应用程序启动时,它调用GSEventRunModalwhich 又调用CFRunLoopRunInMode应用程序的当前运行模式,kCFRunLoopDefaultMode.
  3. 当用户开始滑动滚动视图时,滚动视图特定的平移手势识别器进入 Began 状态并触发[[UIApplication sharedApplication] pushRunMode:UITrackingRunLoopMode requester:self]
  4. 该方法将字符串压入堆栈,看到运行模式不同,并调用CFRunLoopStopGSEventRunModal.
  5. GSEventRunModal循环并CFRunLoopRunInMode再次调用,这次使用UITrackingRunLoopMode.
  6. 当减速完成时,手势识别器调用[[UIApplication sharedApplication] popRunMode:UITrackingRunLoopMode requester:self]和类似的步骤发生。

我长期运行的函数调用runUntilDate:它的问题永远不会终止,所以CFRunLoopStop没有效果。采取的一种方法是混合pushRunMode:requester:popRunMode:requester:用我自己的内部逻辑做类似的事情。

由于我不会收到任何实际的用户交互,我实际采用的方法是将触摸移动事件发送到应用程序并检查它上面有什么手势识别器。如果有UIScrollViewPanGestureRecognizer进入Began阶段,则用于拖动的其余运行循环使用 完成UITrackingRunLoopMode。触摸模拟完成后,我会继续在该模式下运行,直到scrollView.dragging == NO.

您可以在https://github.com/bnickel/KIF/blob/kif-next/Additions/UIView-KIFAdditions.m#L368看到它的实际效果

于 2013-07-01T17:02:12.573 回答