自定义事件循环和UIKit控件。 Apple的事件循环有多么神奇?

时间:2010-04-07 12:37:05

标签: iphone animation event-handling uikit event-loop

有没有人知道或有很好的链接来解释iPhone的事件循环在幕后做什么?

我们在基于OpenGL的iPhone游戏框架中使用自定义事件循环。它调用我们的游戏渲染系统,使用CFRunLoopRunInMode调用presentRenderbuffer和泵事件。有关详细信息,请参阅以下代码

当我们不使用UIKit控件时(作为证明,尝试Facetap,我们的第一个发布的游戏)它很有效。

然而,当使用UIKit控件时,几乎的所有内容都可以正常工作,但并不完整。具体来说,滚动UIKit控件无法正常工作。

例如,让我们考虑以下场景。

  • 我们在自己的视图上显示UIImagePickerController。
  • UIImagePickerController涵盖我们的自定义视图
  • 我们也暂停自己的渲染,但继续使用自定义事件循环。

如上所述,一切都有效,滚动除外。

  • 挑选照片有效。
  • 深入了解相册作品和过渡动画 是否流畅。
  • 尝试滚动相册视图时,视图会跟随您的手指。

问题:滚动时,抬起手指后立即停止滚动。通常情况下,它会根据您的移动速度顺利进行,但不会在我们使用自定义事件循环时继续。似乎iPhone的事件循环正在做一些与我们自己没有实现的UIKit滚动相关的魔术。

现在,通过使用Apple的事件循环并通过NSTimer回调调用我们自己的渲染,我们可以让UIKit控件与我们自己的系统一起工作得很好。但是,我仍然想了解,在我们的自定义事件循环中未实现的iPhone事件循环中可能发生的事情。

- (void)customEventLoop { OBJC_METHOD;
  float excess = 0.0f;
  while(isRunning) {
    animationInterval = 1.0f / openGLapp->ticks_per_second();

    // Calculate the target time to be used in this run of loop
    float wait = max(0.0, animationInterval - excess); 
    Systemtime target = Systemtime::now().after_seconds(wait);

    Scope("event loop");

    NSAutoreleasePool* pool = [[ NSAutoreleasePool alloc] init];

    // Call our own render system and present render buffer 
    [self drawView];
    // Pump system events
    [self handleSystemEvents:target];

    [pool release];

    excess = target.seconds_to_now();
  }
}

- (void)drawView { OBJC_METHOD;

  // call our own custom rendering
  bool bind = openGLapp->app_render();

  // bind the buffer to be THE renderbuffer and present its contents
  if (bind) {
    opengl::bind_renderbuffer(renderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
  }
}

- (void) handleSystemEvents:(Systemtime)target { OBJC_METHOD;
  SInt32 reason = 0;
  double time_left = target.seconds_since_now();
  if (time_left <= 0.0) {
    while((reason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE)) == kCFRunLoopRunHandledSource) {}
  } else {
    float dt = time_left;
    while((reason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, dt, FALSE)) == kCFRunLoopRunHandledSource) {
      double time_left = target.seconds_since_now();
      if (time_left <= 0.0) break;
      dt = (float) time_left;
    }
  }
}

1 个答案:

答案 0 :(得分:4)

如果NSLog [[NSRunLoop currentRunLoop] currentMode][UIScrollView setContentOffset:] [UIScrollView isDecelerating]UITrackingRunLoopMode,您会看到kCFRunLoopDefaultMode

通常,系统将在主UI线程运行循环中使用除NSTimer之外的模式,其中只有一些是记录的。获得完整系统行为的唯一方法是与主线程上的系统运行循环合作。

您可以尝试使用CFRunLoopRunInMode并让系统给您打电话,而不是自己拨打NSTimer。 {{1}}随着时间的推移可以自由运行,当没有显示其他UI时,除了调用计时器之外,系统运行循环不会执行任何操作。

另一种方法是在显示系统控件时从customEventLoop函数返回,并在恢复自定义UI时再次调用它。