我正在开发一个在应用程序主线程中运行的UI测试框架(KIF-next)。基本过程是:
runUntilDate:
旋转主循环0.1秒。这种方法效果非常好。您可以模拟点击事件,可以滑动滑块,观看警报视图的动画效果,scrollToRowAtIndexPath:。除了一个特定的场景,拖动滚动视图,一切正常。
您可以上下拖动滚动视图,但是当您松开手指时,没有任何反应。此外,触摸事件不再被其他视图识别,您可以做的就是拖动滚动视图。
如果这个执行模式结束并且代码退出,滚动视图将执行它们的反弹/减速,但这不是一个选项,因为我在执行顺序执行的ocunit中工作。
我的问题:为什么会发生这种情况以及如何解决?
我有两个演示:
使用此代码,您可以测试您的应用程序并确认一切正常,直到滚动。在那之后,没有任何作用。
- (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;
}
如果您将此代码连接到某个按钮,如果您先点击该按钮然后滚动,您将看到与上述相同的效果。一旦计时器完成,您将看到您的应用程序恢复生机。
- (IBAction)spinButtonClicked:(id)sender
{
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
}
答案 0 :(得分:8)
正如Dave指出的那样,问题与运行循环模式有关。应用程序将主要驻留在kCFRunLoopDefaultMode
模式,但在用户开始滚动时切换到UITrackingRunLoopMode
,当滚动视图完成减速时再切换回UIApplication
。这似乎是为了防止正常计时器在滚动期间触发。 (您可以在Safari中看到此动画,其中动画在滚动期间停止更新。)
机制似乎如下。
GSEventRunModal
有一个堆栈,用于跟踪应用程序请求的当前运行循环模式。CFRunLoopRunInMode
,然后使用当前的应用程序运行模式kCFRunLoopDefaultMode
调用[[UIApplication sharedApplication] pushRunMode:UITrackingRunLoopMode requester:self]
。CFRunLoopStop
。GSEventRunModal
创建的运行循环上调用GSEventRunModal
。CFRunLoopRunInMode
循环并再次致电UITrackingRunLoopMode
,这次是[[UIApplication sharedApplication] popRunMode:UITrackingRunLoopMode requester:self]
。runUntilDate:
并执行类似的步骤。我的长时间运行函数调用CFRunLoopStop
它永远不会终止,因此pushRunMode:requester:
无效。一种方法是调动popRunMode:requester:
和UIScrollViewPanGestureRecognizer
并使用我自己的内部逻辑做类似的事情。
我实际采用的方法,因为我不会接收任何实际的用户交互,就是将触摸移动事件发送到应用程序并检查其上有哪些gestureRecognizer。如果有Began
进入UITrackingRunLoopMode
阶段,则拖动的剩余运行循环将使用scrollView.dragging == NO
完成。触摸模拟完成后,我将继续以该模式运行,直至{{1}}。
您可以在https://github.com/bnickel/KIF/blob/kif-next/Additions/UIView-KIFAdditions.m#L368
看到它的实际效果