Miałem problem polegający na tym, że miałem serię nakładających się sekwencji CATransition / CAAnimation, z których wszystkie potrzebowałem do wykonywania niestandardowych operacji po zatrzymaniu animacji, ale chciałem tylko jednego delegata obsługi dla animationDidStop.
Jednak miałem problem, wydaje się, że nie ma sposobu na unikalną identyfikację każdego przejścia CATransition / CAAnimation w delegacie AnimationDidStop.
Rozwiązałem ten problem za pomocą systemu klucza / wartości ujawnionego w ramach CAAnimation.
Po uruchomieniu animacji użyj metody setValue w CATransition / CAAnimation, aby ustawić swoje identyfikatory i wartości, które mają być używane podczas uruchamiania animacjiDidStop:
-(void)volumeControlFadeToOrange
{
CATransition* volumeControlAnimation = [CATransition animation];
[volumeControlAnimation setType:kCATransitionFade];
[volumeControlAnimation setSubtype:kCATransitionFromTop];
[volumeControlAnimation setDelegate:self];
[volumeControlLevel setBackgroundImage:[UIImage imageNamed:@"SpecialVolume1.png"] forState:UIControlStateNormal];
volumeControlLevel.enabled = true;
[volumeControlAnimation setDuration:0.7];
[volumeControlAnimation setValue:@"Special1" forKey:@"MyAnimationType"];
[[volumeControlLevel layer] addAnimation:volumeControlAnimation forKey:nil];
}
- (void)throbUp
{
doThrobUp = true;
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionFade];
[animation setSubtype:kCATransitionFromTop];
[animation setDelegate:self];
[hearingAidHalo setBackgroundImage:[UIImage imageNamed:@"m13_grayglow.png"] forState:UIControlStateNormal];
[animation setDuration:2.0];
[animation setValue:@"Throb" forKey:@"MyAnimationType"];
[[hearingAidHalo layer] addAnimation:animation forKey:nil];
}
W twojej animacji DidStop delegata:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag{
NSString* value = [theAnimation valueForKey:@"MyAnimationType"];
if ([value isEqualToString:@"Throb"])
{
//... Your code here ...
return;
}
if ([value isEqualToString:@"Special1"])
{
//... Your code here ...
return;
}
//Add any future keyed animation operations when the animations are stopped.
}
Innym aspektem tego jest to, że pozwala to na utrzymanie stanu w systemie parowania klucz-wartość zamiast konieczności przechowywania go w klasie delegatów. Im mniej kodu, tym lepiej.
Koniecznie zapoznaj się z odniesieniem firmy Apple na temat kodowania pary klucz-wartość .
Czy istnieją lepsze techniki identyfikacji przejścia CAAnimation / CAT w delegacie AnimationDidStop?
Dzięki, - Batgar
źródło
CAAnimation
todelegate
jest mocne, więc może być konieczne ustawienie go,nil
aby uniknąć zachowania cykli!Odpowiedzi:
Technika Batgara jest zbyt skomplikowana. Dlaczego nie skorzystać z parametru forKey w addAnimation? Miał właśnie taki cel. Po prostu wyłącz wywołanie setValue i przenieś ciąg klucza do wywołania addAnimation. Na przykład:
Następnie w wywołaniu zwrotnym animacjiDidStop możesz zrobić coś takiego:
źródło
anim.removedOnCompletion = NO;
tak, aby nadal istniała, gdy-animationDidStop:finished:
zostanie wywołana.Właśnie wymyśliłem jeszcze lepszy sposób tworzenia kodu uzupełniającego dla CAAnimations:
Stworzyłem typedef dla bloku:
I klucz, którego używam, aby dodać blok do animacji:
Następnie, jeśli chcę uruchomić kod zakończenia animacji po zakończeniu CAAnimation, ustawiam siebie jako delegata animacji i dodaję blok kodu do animacji za pomocą setValue: forKey:
Następnie implementuję metodę animationDidStop: finish:, która sprawdza blok pod określonym kluczem i wykonuje go, jeśli zostanie znaleziony:
Piękno tego podejścia polega na tym, że możesz napisać kod czyszczenia w tym samym miejscu, w którym tworzysz obiekt animacji. Co więcej, ponieważ kod jest blokiem, ma dostęp do zmiennych lokalnych w otaczającym zakresie, w którym jest zdefiniowany. Nie musisz majstrować przy ustawianiu słowników userInfo lub innych tego typu bzdurach i nie musisz pisać stale rosnącej animacji DidStop: finish: metoda, która staje się coraz bardziej złożona, gdy dodajesz różne rodzaje animacji.
Prawdę mówiąc, CAAnimation powinno mieć wbudowaną właściwość bloku uzupełniania i obsługę systemu umożliwiającą automatyczne wywoływanie jej, jeśli taka jest określona. Jednak powyższy kod zapewnia tę samą funkcjonalność z zaledwie kilkoma wierszami dodatkowego kodu.
źródło
theBlock();
wywołaniu i wydaje mi się, że jest to spowodowane tym, że zakres bloku został zniszczony.Drugie podejście zadziała tylko wtedy, gdy wyraźnie ustawisz animację tak, aby nie była usuwana po zakończeniu przed jej uruchomieniem:
Jeśli tego nie zrobisz, Twoja animacja zostanie usunięta przed zakończeniem, a wywołanie zwrotne nie znajdzie jej w słowniku.
źródło
Wszystkie inne odpowiedzi są zbyt skomplikowane! Dlaczego po prostu nie dodasz własnego klucza, aby zidentyfikować animację?
To rozwiązanie jest bardzo proste, wystarczy dodać własny klucz do animacji (w tym przykładzie identyfikator animacji )
Wstaw tę linię, aby zidentyfikować animację1 :
a to w celu zidentyfikowania animacji2 :
Przetestuj to w ten sposób:
Nie wymaga żadnych zmiennych instancji :
źródło
[animation valueForKey:@"animationID"]
Aby wyjaśnić, co wynika z góry (i co mnie tu sprowadziło po kilku zmarnowanych godzinach): nie spodziewaj się, że oryginalny obiekt animacji, który przydzieliłeś, zostanie ci przekazany z powrotem przez
kiedy animacja się kończy, ponieważ
[CALayer addAnimation:forKey:]
tworzy kopię animacji.Możesz polegać na tym, że wartości klucza, które nadałeś swojemu obiektowi animacji, nadal istnieją z równoważną wartością (ale niekoniecznie równoważnością wskaźnika) w obiekcie animacji repliki przekazanym z
animationDidStop:finished:
komunikatem. Jak wspomniano powyżej, użyj KVC, a uzyskasz szeroki zakres do przechowywania i pobierania stanu.źródło
[animation setValue:@"myanim" forKey:@"name"]
a nawet możesz ustawić animowaną warstwę za pomocą[animation setValue:layer forKey:@"layer"]
. Te wartości można następnie pobrać w ramach metod delegata.valueForKey:
wracanil
do mnie, jakiś pomysł dlaczego?Widzę głównie odpowiedzi objc. Zrobię jedną dla Swift 2.3 w oparciu o najlepszą odpowiedź powyżej.
Na początek dobrze będzie przechowywać wszystkie te klucze w prywatnej strukturze, aby można było je bezpiecznie wpisywać, a zmiana w przyszłości nie przyniesie irytujących błędów tylko dlatego, że zapomniałeś zmienić go wszędzie w kodzie:
Jak widać, zmieniłem nazwy zmiennych / animacji, aby było bardziej przejrzyste. Teraz ustawiam te klawisze podczas tworzenia animacji.
(...)
Następnie w końcu obsługa delegata, kiedy animacja się zatrzyma
źródło
IMHO przy użyciu klucza-wartości firmy Apple jest eleganckim sposobem na zrobienie tego: ma to w szczególności umożliwić dodawanie danych specyficznych dla aplikacji do obiektów.
Inną, znacznie mniej elegancką możliwością jest przechowywanie odniesień do obiektów animacji i porównywanie wskaźników w celu ich identyfikacji.
źródło
Aby sprawdzić, czy 2 obiekty CABasicAnimation są tą samą animacją, używam funkcji keyPath, aby zrobić dokładnie to.
if ([ścieżka_klucza animacjiA] == [ścieżka_klucza animacjiB])
źródło
Lubię używać
setValue:forKey
: aby zachować odniesienie do animowanego widoku, jest to bezpieczniejsze niż próba jednoznacznej identyfikacji animacji na podstawie identyfikatora, ponieważ ten sam rodzaj animacji można dodać do różnych warstw.Te dwa są równoważne:
z tym:
aw metodzie delegata:
źródło
Xcode 9 Swift 4.0.0
Możesz użyć wartości kluczowych, aby powiązać animację dodaną do animacji zwróconej w metodzie delegata animationDidStop.
Zadeklaruj słownik, aby zawierał wszystkie aktywne animacje i powiązane uzupełnienia:
Dodając animację, ustaw dla niej klucz:
W animacjiDidStop dzieje się magia:
źródło