2010-04-07 22 views
9

Czy ktoś zna lub ma dobre linki, które wyjaśniają, co pętla zdarzeń iPhone'a działa pod maską?Niestandardowa pętla zdarzeń i elementy sterujące UIKit. Jaką dodatkową magiczną pętlę zdarzeń Apple?

Używamy niestandardowej pętli zdarzeń w ramach naszego iPhone gier opartych na OpenGL. Wywołuje nasz system renderowania gier, wywołuje presentRenderbuffer i pompuje zdarzenia za pomocą metody CFRunLoopRunInMode. Zobacz kod poniżej, aby uzyskać szczegółowe informacje.

Działa dobrze, gdy nie używamy kontrolek UIKit (jako dowód, wypróbuj Facetap, naszą pierwszą wydaną grę).

Jednak przy użyciu kontrolek UIKit wszystko działa prawidłowo, ale nie całkiem. W szczególności przewijanie kontrolek UIKit nie działa poprawnie.

Na przykład rozważmy następujący scenariusz.

  • Pokazujemy UIImagePickerController na naszym własnym widoku.
  • UIImagePickerController obejmuje nasz widok niestandardowy
  • Wstrzymujemy również nasze własne renderowanie, ale nadal używamy niestandardowej pętli zdarzeń.

Jak już powiedziano, wszystko działa, z wyjątkiem przewijania.

  • Wybieranie zdjęć działa.
  • Wiercenie do albumów fotograficznych działa i animacje przejścia gładka.
  • Podczas próby przewinięcia widoku albumu zdjęć widok podąża za palcem.

Problem: podczas przewijania przewijanie zatrzymuje się natychmiast po podniesieniu palca. Zwykle działa płynnie w oparciu o szybkość twojego ruchu, ale nie wtedy, gdy używamy niestandardowej pętli zdarzeń. Wygląda na to, że pętla zdarzeń iPhone'a robi magię związaną z przewijaniem UIKit, którego sami nie zaimplementowaliśmy.

Teraz możemy uzyskać kontrolę UIKit, aby działała dobrze i elegancko razem z naszym własnym systemem, używając pętli zdarzeń Apple i wywołując nasze własne renderowanie za pośrednictwem wywołań NSTimer. Jednak nadal chciałbym zrozumieć, co się dzieje w pętli zdarzeń iPhone'a, która nie jest zaimplementowana w naszej niestandardowej pętli zdarzeń.

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

Odpowiedz

4

Jeśli NSLog[[NSRunLoop currentRunLoop] currentMode] z [UIScrollView setContentOffset:] gdy [UIScrollView isDecelerating] to prawda, że ​​będzie zobaczyć UITrackingRunLoopMode.

Ogólnie rzecz biorąc, system użyje trybów poza kCFRunLoopDefaultMode w głównej pętli wątków UI, z których tylko niektóre są udokumentowane. Jedynym sposobem uzyskania pełnego zachowania systemu jest współpraca z pętlą uruchamiania systemu w głównym wątku.

Możesz spróbować użyć NSTimer i pozwolić systemowi zadzwonić, zamiast samemu zadzwonić pod numer CFRunLoopRunInMode. Numer NSTimer można uruchamiać z biegiem czasu, a gdy nie jest wyświetlany żaden inny interfejs użytkownika, pętla uruchamiania systemu nie będzie wykonywać żadnych czynności poza wywołaniem licznika czasu.

Alternatywą jest powrót z funkcji customEventLoop, gdy wyświetlane są elementy sterujące systemu, i wywołanie go ponownie po wznowieniu niestandardowego interfejsu użytkownika.

+0

Użyłem timerów do gry OpenGL i pomimo tego, co możesz najpierw pomyśleć, uzyskałem najwyższą liczbę klatek na sekundę przy użyciu tej metody. Wydaje się, że gra się znacznie lepiej z główną pętlą zdarzeń. –

+0

Drawonward, dziękuję, uruchom tryby pętli wyjaśnia to wszystko. Stosujemy również podejście oparte na NSTimer i ma pewne anomalie z szybkimi animacjami. W systemie iPhone OS 3.1 dostępny jest program CADisplayLink, który może pomóc w rozwiązaniu problemu. –

+0

z jakiegoś powodu nie mogę oznaczyć odpowiedzi jako zaakceptowanej. Pytanie to miało wcześniej nagrodę, ale odpowiedziałeś po zamknięciu polowania na bounty i wydaje się, że Stackoverflow uniemożliwia mi zaznaczenie odpowiedzi zaakceptowanej po tym. Dziwne. –

Powiązane problemy