Nie zanalizowałem Twojego kodu, aby mieć 100% pewności, dlaczego [[container.layer presentationLayer] frame]
może nie zwrócić tego, czego oczekujesz. Ale widzę kilka problemów.
Jednym z oczywistych problemów jest to, że moveDown:
nie deklaruje movePath
. Jeśli movePath
jest zmienną instancji, prawdopodobnie chcesz ją wyczyścić lub utworzyć nową instancję za każdym razem, gdy wywoływana jest nazwa moveDown:
, ale nie widzę, żebyś to robił.
Mniej oczywistym problemem jest to, że (sądząc po korzystaniu z removedOnCompletion
i fillMode
, pomimo korzystania z presentationLayer
) najwyraźniej nie rozumiesz, jak działa Core Animation. Okazuje się to zaskakująco powszechne, więc wybacz mi, jeśli się mylę. W każdym razie, czytaj dalej, ponieważ wyjaśnię, jak działa Core Animation, a następnie jak rozwiązać problem.
W programie Core Animation obiekt warstwy, z którym zwykle pracujesz, jest warstwą modelu . Po dołączeniu animacji do warstwy program Core Animation tworzy kopię warstwy modelu o nazwie warstwa prezentacji, a animacja zmienia właściwości warstwy prezentacji w czasie. Animacja nigdy nie zmienia właściwości warstwy modelu.
Po zakończeniu animacji i (domyślnie) usunięciu warstwa prezentacji zostanie zniszczona, a wartości właściwości warstwy modelu zostaną ponownie zastosowane. Tak więc warstwa na ekranie wydaje się "cofać" do swojej pierwotnej pozycji/koloru/cokolwiek.
Powszechnym, ale źle sposób, aby naprawić to, aby ustawić animację na removedOnCompletion
do NO
i jej fillMode
do kCAFillModeForwards
. Gdy to zrobisz, warstwa prezentacji będzie się kręcić, więc nie będzie już "cofania" na ekranie. Problem polega na tym, że teraz warstwa prezentacji kręci się z różnymi wartościami niż warstwa modelu. Jeśli poprosisz warstwę modelu (lub widok, który jest jej właścicielem) o wartość animowanej właściwości, otrzymasz wartość inną niż na ekranie. A jeśli spróbujesz animować nieruchomość ponownie, animacja prawdopodobnie rozpocznie się od niewłaściwego miejsca.
Aby animować właściwość warstwy i sprawić, że będzie "trzymać", należy zmienić wartość właściwości warstwy modelu, a następnie zastosować animację. W ten sposób, gdy animacja zostanie usunięta, a warstwa prezentacji zniknie, warstwa na ekranie będzie wyglądać dokładnie tak samo, ponieważ warstwa modelu ma te same wartości właściwości, co w warstwie prezentacji po zakończeniu animacji.
Teraz nie wiem, dlaczego używasz klatki kluczowej do animowania ruchu prostoliniowego lub dlaczego używasz grupy animacji. Nie wydaje się konieczne tutaj.A twoje dwie metody są praktycznie identyczne, więc niech się czynnik wspólny kod:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromValue];
animation.toValue = [NSValue valueWithCGPoint:toValue];
animation.duration = 2;
[layer addAnimation:animation forKey:animation.keyPath];
}
Zauważ, że dajemy animacji klucza kiedy go dodać do warstwy. Ponieważ za każdym razem używamy tego samego klucza, każda nowa animacja zastąpi (usunie) poprzednią animację, jeśli poprzednia animacja jeszcze się nie zakończyła.
Oczywiście, jak tylko grać z tym, przekonasz się, że jeśli moveUp:
gdy moveDown:
jest tylko w połowie gotowy, animacja moveUp:
pojawi się z połową prędkości, ponieważ nadal ma trwać 2 sekundy ale tylko o połowę mniej w podróży. Powinniśmy naprawdę obliczyć czas w oparciu o odległości do przejechania:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromValue];
animation.toValue = [NSValue valueWithCGPoint:toValue];
animation.duration = 2.0 * (toValue.y - fromValue.y)/(y - baseY);
[layer addAnimation:animation forKey:animation.keyPath];
}
Jeśli naprawdę muszą to być animacja keypath w grupie animacji, Twoje pytanie powinno pokazać nam dlaczego trzeba te rzeczy . W każdym razie, to działa z tych rzeczy, TOO:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:fromValue];
[path addLineToPoint:toValue];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.path = path.CGPath;
animation.duration = 2.0 * (toValue.y - fromValue.y)/(y - baseY);
CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = animation.duration;
group.animations = @[animation];
[layer addAnimation:group forKey:animation.keyPath];
}
można znaleźć pełny kod dla mojego programu testowego in this gist. Po prostu utwórz nowy projekt aplikacji Single View Application i zastąp zawartość zawartego w nim fragmentu na ViewController.m
.
Dziękuję bardzo. Jestem nowy w używaniu animacji i to pomogło tonę! Dam mu szansę! – Matt
Zobacz film [Sesja 421 - Core Animation Essentials] (https://developer.apple.com/videos/wwdc/2011/?id=421) wideo z WWDC 2011. To tylko godzina i jest ** wyjątkowo ** informacyjny. –
Niesamowita odpowiedź +1! – Till