У меня возникла проблема, когда у меня была серия перекрывающихся последовательностей CATransition / CAAnimation, все из которых мне нужно было выполнять пользовательские операции при остановке анимации, но мне нужен был только один обработчик делегата для animationDidStop.
Однако у меня возникла проблема: похоже, не было способа однозначно идентифицировать каждый CATransition / CAAnimation в делегате animationDidStop.
Я решил эту проблему с помощью системы ключ / значение, представленной как часть CAAnimation.
Когда вы запускаете анимацию, используйте метод setValue в CATransition / CAAnimation, чтобы установить идентификаторы и значения, которые будут использоваться при срабатывании animationDidStop:
-(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];
}
В вашем делегате animationDidStop:
- (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.
}
Другой аспект этого заключается в том, что он позволяет вам сохранять состояние в системе пар ключ-значение вместо того, чтобы хранить его в вашем классе делегата. Чем меньше кода, тем лучше.
Обязательно ознакомьтесь со справочником Apple по кодированию пар ключевых значений .
Есть ли лучшие методы идентификации CAAnimation / CATransition в делегате animationDidStop?
Спасибо, Батгар
источник
CAAnimation
«sdelegate
сильна, так что вам , возможно , придется установить его ,nil
чтобы избежать сохранения циклов!Ответы:
Техника Батгара слишком сложна. Почему бы не воспользоваться параметром forKey в addAnimation? Он был предназначен именно для этого. Просто возьмите вызов setValue и переместите ключевую строку в вызов addAnimation. Например:
Затем в обратном вызове animationDidStop вы можете сделать что-то вроде:
источник
anim.removedOnCompletion = NO;
так, чтобы он все еще существовал при-animationDidStop:finished:
вызове.Я только что придумал еще лучший способ сделать код завершения для CAAnimations:
Я создал typedef для блока:
И ключ, который я использую для добавления блока к анимации:
Затем, если я хочу запустить код завершения анимации после завершения CAAnimation, я устанавливаю себя в качестве делегата анимации и добавляю блок кода к анимации, используя setValue: forKey:
Затем я реализую метод animationDidStop: finished:, который проверяет наличие блока по указанному ключу и выполняет его, если он найден:
Прелесть этого подхода в том, что вы можете написать код очистки в том же месте, где вы создаете объект анимации. Еще лучше, поскольку код является блоком, он имеет доступ к локальным переменным в охватывающей области, в которой он определен. Вам не нужно возиться с настройкой словарей userInfo или другой подобной ерунды, и вам не нужно писать постоянно растущий метод animationDidStop: finished:, который становится все более и более сложным по мере добавления различных видов анимации.
По правде говоря, CAAnimation должен иметь встроенное свойство блока завершения и поддержку системы для его автоматического вызова, если он указан. Однако приведенный выше код дает вам ту же функциональность с помощью всего лишь нескольких строк дополнительного кода.
источник
theBlock();
вызова, и я считаю, что это связано с тем, что область действия блока была уничтожена.Второй подход будет работать только в том случае, если вы явно укажете, что анимация не удаляется по завершении перед ее запуском:
Если вы этого не сделаете, ваша анимация будет удалена до того, как завершится, и обратный вызов не найдет ее в словаре.
источник
Все остальные ответы слишком сложны! Почему бы вам просто не добавить свой собственный ключ для идентификации анимации?
Это очень простое решение, все, что вам нужно, это добавить свой собственный ключ к анимации (в этом примере animationID)
Вставьте эту строку, чтобы идентифицировать animation1 :
и это для идентификации animation2 :
Протестируйте это так:
Не требует никаких переменных экземпляра :
источник
[animation valueForKey:@"animationID"]
Чтобы пояснить, что подразумевается выше (и что привело меня сюда после нескольких потраченных впустую часов): не ожидайте, что исходный объект анимации, который вы выделили, передан вам обратно
когда анимация закончится, потому что
[CALayer addAnimation:forKey:]
делает копию вашей анимации.На что вы можете положиться, так это на то, что ключевые значения, которые вы дали своему объекту анимации, все еще существуют с эквивалентным значением (но не обязательно эквивалентностью указателя) в реплике объекта анимации, переданном с
animationDidStop:finished:
сообщением. Как упоминалось выше, используйте KVC, и вы получите достаточно возможностей для хранения и извлечения состояния.источник
[animation setValue:@"myanim" forKey:@"name"]
и вы даже можете установить анимированный слой с помощью[animation setValue:layer forKey:@"layer"]
. Затем эти значения можно получить в методах делегата.valueForKey:
возвращаетсяnil
для меня, любая идея почему?Я вижу в основном ответы objc, которые я сделаю для быстрого 2.3 на основе лучшего ответа выше.
Для начала было бы хорошо хранить все эти ключи в частной структуре, чтобы она была безопасной по типу, и ее изменение в будущем не принесет вам досадных ошибок только потому, что вы забыли изменить его везде в коде:
Как видите, я изменил имена переменных / анимаций, чтобы они были более понятными. Теперь устанавливаем эти ключи при создании анимации.
(...)
Затем, наконец, обработка делегата, когда анимация останавливается
источник
ИМХО, использование ключа-значения Apple - это элегантный способ сделать это: он специально предназначен для добавления данных, специфичных для приложения, к объектам.
Другая, менее элегантная возможность - сохранить ссылки на объекты анимации и выполнить сравнение указателей для их идентификации.
источник
Чтобы проверить, являются ли 2 объекта CABasicAnimation одной и той же анимацией, я использую функцию keyPath, чтобы сделать именно это.
if ([animationA keyPath] == [animationB keyPath])
источник
Мне нравится использовать
setValue:forKey
: для сохранения ссылки на вид, который я анимирую, это более безопасно, чем пытаться однозначно идентифицировать анимацию на основе идентификатора, потому что один и тот же вид анимации может быть добавлен к разным слоям.Эти два эквивалента:
с этим:
и в методе делегата:
источник
Xcode 9 Swift 4.0
Вы можете использовать ключевые значения, чтобы связать анимацию, которую вы добавили, с анимацией, возвращенной в методе делегата animationDidStop.
Объявите словарь, содержащий все активные анимации и связанные дополнения:
Когда вы добавляете свою анимацию, установите для нее ключ:
В animationDidStop происходит волшебство:
источник