9

誰かがiPhoneのイベントループが内部で何をするかを説明する良いリンクを知っているか持っていますか?

OpenGLベースのiPhoneゲームフレームワークでカスタムイベントループを使用しています。ゲームレンダリングシステムを呼び出し、presentRenderbufferを呼び出し、CFRunLoopRunInModeを使用してイベントをポンプします。詳細については、以下のコードを参照してください。

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;
    }
  }
}
4

1 に答える 1

4

いつNSLog [[NSRunLoop currentRunLoop] currentMode]からあなたが本当ならあなたは見るでしょう。[UIScrollView setContentOffset:][UIScrollView isDecelerating]UITrackingRunLoopMode

一般に、システムはメインUIスレッド実行ループ以外のモードを使用しますが、kCFRunLoopDefaultModeその一部のみが文書化されています。完全なシステム動作を実現する唯一の方法は、メインスレッドのシステム実行ループと連携することです。

NSTimer自分自身を呼び出す代わりに、を使用してシステムに電話をかけてもらうことができCFRunLoopRunInModeます。AnNSTimerは時間の経過とともに自由に実行でき、他のUIが表示されない場合、システム実行ループはタイマーを呼び出す以外に何も実行しません。

別の方法は、システムコントロールが表示されている間にcustomEventLoop関数から戻り、カスタムUIを再開するときに再度呼び出すことです。

于 2010-05-11T05:19:48.257 に答える