誰かが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;
}
}
}