2013-03-02 14 views
14

Jestem nowy w programowaniu Objective-C/iOS i staram się zrozumieć, w jaki sposób działa animacja UIView pod maską.Jak korzystać z animacji bloków działających pod maską

Say mam kod jak poniżej:

[UIView animateWithDuration:2.0 animations:^{ 
    self.label.alpha = 1.0; 
}]; 

Rzecz, która zostaje przekazana jako animations argumentem jest blok Objective-C (coś jak lambda/anonimowe funkcji w innych językach), które mogą być wykonywane i następnie zmienia właściwość alpha z label z bieżącej wartości na 1.0.

Jednak blok nie przyjmuje argumentu postępu animacji (powiedzmy, idąc od 0.0 do 1.0 lub od 0 do 1000). Moje pytanie dotyczy tego, w jaki sposób struktura animacji wykorzystuje ten blok do poznawania ramek pośrednich, ponieważ blok określa jedynie stan końcowy.

EDIT: Moje pytania to raczej o pod maską działania animateWithDuration metody zamiast sposobów, aby go używają.

Moja hipoteza jak kod animateWithDuration działa następująco:

  1. animateWithDuration aktywuje jakiś specjalny stan wszystkich obiektów widoku, w którym zmiany te nie są faktycznie wykonywane, ale tylko zarejestrowane.
  2. wykonuje blok, a zmiany są rejestrowane.
  3. zapytuje obiekty widoków o zmieniony stan i zwraca wartości początkowe i docelowe, a tym samym wie, jakie właściwości zmienić iw jakim zakresie dokonać zmian.
  4. oblicza ramki pośrednie na podstawie czasu trwania i wartości początkowych/docelowych i uruchamia animację.

Czy ktoś może rzucić nieco światła na to, czy tak naprawdę działa animateWithDuration?

Odpowiedz

16

Oczywiście nie wiem, co dokładnie dzieje się pod maską, ponieważ UIKit nie jest open-source, a ja nie pracuję w Apple, ale oto kilka pomysłów:

przed blokiem oparte UIView metody animacji zostały wprowadzone, animowanie widoki wyglądał tak, a te metody są rzeczywiście nadal dostępne:

[UIView beginAnimations:nil context:nil]; 
[UIView setAnimationDuration:duration]; 
myView.center = CGPointMake(300, 300); 
[UIView commitAnimations]; 

Wiedząc o tym, możemy realizować naszą własną metodę animacji opartej na bloku tak:

+ (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 
{ 
    [UIView beginAnimations:nil context:nil]; 
    [UIView setAnimationDuration:duration]; 
    animations(); 
    [UIView commitAnimations]; 
} 

... który będzie dokładnie taki sam jak istniejąca metoda animateWithDuration:animations:.

Po usunięciu bloku z równania staje się jasne, że musi istnieć jakiś globalny stan animacji, który następnie będzie używany do animowania zmian jego właściwości (animowalnych), gdy zostaną wykonane w bloku animacji. To musi być jakiś stos, ponieważ możesz mieć zagnieżdżone bloki animacji.

Rzeczywista animacja jest wykonywana przez Rdzeń animacji, który działa na poziomie warstwy - każda UIView ma podkładową CALayer instancji, który jest odpowiedzialny za animacje i tworzenia kompozycji, natomiast widok przeważnie tylko uchwyty zdarzeń dotykowych i koordynuje konwersji systemowych.

Nie będę tutaj szczegółowo omawiał działania animacji rdzenia, dlatego warto przeczytać o tym Core Animation Programming Guide. Zasadniczo jest to system do animowania zmian w drzewie warstw, bez jawnego obliczania każdej klatki kluczowej (a tak naprawdę trudno jest uzyskać wartości pośrednie poza Core Animation, zazwyczaj podajesz od i do wartości, czasu trwania itd. I pozwalasz systemowi zajmij się szczegółami).

Ponieważ UIView jest oparty na CALayer, wiele jego właściwości jest faktycznie zaimplementowanych w warstwie podstawowej. Na przykład, jeśli ustawisz lub otrzymasz view.center, będzie to ta sama nazwa co view.layer.location, a zmiana jednego z nich spowoduje również zmianę drugiego.

Warstwy mogą być wyraźnie animowany z CAAnimation (który jest klasą abstrakcyjną, która ma szereg konkretnych wdrożeń, jak CABasicAnimation dla prostych rzeczy i CAKeyframeAnimation W przypadku bardziej złożonych rzeczy).

Co zatem może zrobić narzędzie ustawiające w celu "magicznego" animowania zmian w bloku animacji? Zobaczmy, czy możemy ponownie wprowadzić jeden z nich, dla uproszczenia, użyjmy setCenter:.

pierwsze, tutaj jest zmodyfikowaną wersją metody my_animateWithDuration:animations: z góry, która wykorzystuje globalną CATransaction, tak, że możemy znaleźć się w naszym setCenter: metody jak długo animacja ma wziąć:

- (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 
{ 
    [CATransaction begin]; 
    [CATransaction setAnimationDuration:duration]; 

    animations(); 

    [CATransaction commit]; 
} 

Należy pamiętać, że nie używamy już beginAnimations:... i commitAnimations, więc bez robienia czegokolwiek innego, nic nie będzie animowane.

A teraz zastępują setCenter: w UIView podklasy:

@interface MyView : UIView 
@end 

@implementation MyView 
- (void)setCenter:(CGPoint)position 
{ 
    if ([CATransaction animationDuration] > 0) { 
     CALayer *layer = self.layer; 
     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
     animation.fromValue = [layer valueForKey:@"position"]; 
     animation.toValue = [NSValue valueWithCGPoint:position]; 
     layer.position = position; 
     [layer addAnimation:animation forKey:@"position"]; 
    } 
} 
@end 

Tutaj założyć wyraźny animację używając Core Animation, które animuje dolnej warstwie za location nieruchomości. Czas trwania animacji zostanie automatycznie pobrany z CATransaction. Spróbujmy go:

MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 
myView.backgroundColor = [UIColor redColor]; 
[self.view addSubview:myView]; 

[self my_animateWithDuration:4.0 animations:^{ 
    NSLog(@"center before: %@", NSStringFromCGPoint(myView.center)); 
    myView.center = CGPointMake(300, 300); 
    NSLog(@"center after : %@", NSStringFromCGPoint(myView.center)); 
}]; 

Nie mówię, że to jest dokładnie to, w jaki sposób działa system UIView animacji, to jest po prostu pokazać, jak to mogło pracę w zasadzie.

1

Wartości pośrednie dla ramki nie są określone; animacja wartości (w tym przypadku alfa, ale także kolorów, pozycji itp.) jest generowana automatycznie pomiędzy wcześniej ustawioną wartością a wartością docelową ustawioną w bloku animacji. Krzywą można wpływać, określając opcje za pomocą animateWithDuration:delay:options:animations:completion: (domyślnie jest to UIViewAnimationOptionCurveEaseInOut, tj. Szybkość zmiany wartości przyspiesza i zwalnia).

Należy pamiętać, że wszystkie wcześniej ustawione animowane zmiany wartości zostaną zakończone jako pierwsze, tj. Każdy blok animacji określa nową animację od poprzedniej wartości końcowej do nowej. Możesz podać UIViewAnimationOptionBeginFromCurrentState, aby rozpocząć nową animację od stanu animacji już w toku.

0

Spowoduje to zmianę właściwości określonej wewnątrz bloku z bieżącej wartości na dowolną podaną wartość i wykona ją liniowo w czasie trwania.

Jeśli oryginalna wartość alfa wynosiła 0, zanikłaby ona na etykiecie w ciągu 2 sekund. Jeśli oryginalne wartości były już 1,0, nie zobaczyłbyś żadnego efektu.

Pod maską, UIView dba o ustalenie, ile klatek animacji ma nastąpić zmiana.

Można także zmienić szybkość, z jaką następuje zmiana, określając krzywą dynamiki jako UIViewAnimationOption. Ponownie, UIView obsługuje dla ciebie animację.

Powiązane problemy