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.