Ответ 1
В дополнение к приведенному ниже (в котором мы захватываем текущее состояние с уровня представления, останавливаем анимацию, reset текущее состояние с сохраненного уровня представления и инициируем новую анимацию), существует гораздо более простое решение.
Если вы делаете блок-анимацию, если хотите остановить анимацию и запустить новую анимацию в версиях iOS до 8.0, вы можете просто использовать опцию UIViewAnimationOptionBeginFromCurrentState
. (Эффективно в iOS 8, поведение по умолчанию заключается не только в том, чтобы начать с текущего состояния, но и сделать это таким образом, который отражает как текущее местоположение, так и текущую скорость, что делает его в значительной степени ненужным беспокоиться об этой проблеме вообще См. Видео WWDC 2014 Создание прерывистых и реагирующих взаимодействий для получения дополнительной информации.)
[UIView animateWithDuration:3.0
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction
animations:^{
// specify the new `frame`, `transform`, etc. here
}
completion:NULL];
Вы можете добиться этого, остановив текущую анимацию и запустив новую анимацию, с которой остановился текущий. Вы можете сделать это с помощью Quartz 2D:
-
Добавьте QuartzCore.framework в свой проект, если вы еще этого не сделали. (В современных версиях Xcode часто не нужно явно делать это, поскольку оно автоматически связано с проектом.)
-
Импортируйте необходимый заголовок, если он еще не был (опять же, не нужен в современных версиях Xcode):
#import <QuartzCore/QuartzCore.h>
-
Остановите свой код для существующей анимации:
[self.subview.layer removeAllAnimations];
-
Получить ссылку на текущий уровень представления (т.е. состояние представления, поскольку оно точно в данный момент):
CALayer *currentLayer = self.subview.layer.presentationLayer;
-
Reset
transform
(илиframe
или что-то еще) в соответствии с текущим значением вpresentationLayer
:self.subview.layer.transform = currentLayer.transform;
-
Теперь оживите из этого
transform
(илиframe
или что-то еще) новое значение:[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ self.subview.layer.transform = newTransform; } completion:NULL];
Объединяя все это, вот процедура, которая переключает масштаб преобразования от 2.0x для идентификации и возврата:
- (IBAction)didTouchUpInsideAnimateButton:(id)sender
{
CALayer *currentLayer = self.subview.layer.presentationLayer;
[self.subview.layer removeAllAnimations];
self.subview.layer.transform = currentLayer.transform;
CATransform3D newTransform;
self.large = !self.large;
if (self.large)
newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0);
else
newTransform = CATransform3DIdentity;
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.layer.transform = newTransform;
}
completion:NULL];
}
Или если вы хотите переключать размеры frame
от 100x100 до 200x200 и обратно:
- (IBAction)didTouchUpInsideAnimateButton:(id)sender
{
CALayer *currentLayer = self.subview.layer.presentationLayer;
[self.subview.layer removeAllAnimations];
CGRect newFrame = currentLayer.frame;
self.subview.frame = currentLayer.frame;
self.large = !self.large;
if (self.large)
newFrame.size = CGSizeMake(200.0, 200.0);
else
newFrame.size = CGSizeMake(100.0, 100.0);
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.frame = newFrame;
}
completion:NULL];
}
Кстати, хотя для очень быстрой анимации, как правило, не имеет большого значения для медленных анимаций, например, для вас, вы можете установить длительность анимации обратного воспроизведения так же, как и то, как далеко продвинулись в своем текущая анимация (например, если у вас 0,5 секунды в анимацию на 3,0 секунды, когда вы отменяете, вы, вероятно, не захотите занять 3,0 секунды, чтобы отменить эту небольшую часть анимации, которую вы сделали до сих пор, а всего лишь 0,5 секунд). Таким образом, это может выглядеть так:
- (IBAction)didTouchUpInsideAnimateButton:(id)sender
{
CFTimeInterval duration = kAnimationDuration; // default the duration to some constant
CFTimeInterval currentMediaTime = CACurrentMediaTime(); // get the current media time
static CFTimeInterval lastAnimationStart = 0.0; // media time of last animation (zero the first time)
// if we previously animated, then calculate how far along in the previous animation we were
// and we'll use that for the duration of the reversing animation; if larger than
// kAnimationDuration that means the prior animation was done, so we'll just use
// kAnimationDuration for the length of this animation
if (lastAnimationStart)
duration = MIN(kAnimationDuration, (currentMediaTime - lastAnimationStart));
// save our media time for future reference (i.e. future invocations of this routine)
lastAnimationStart = currentMediaTime;
// if you want the animations to stay relative the same speed if reversing an ongoing
// reversal, you can backdate the lastAnimationStart to what the lastAnimationStart
// would have been if it was a full animation; if you don't do this, if you repeatedly
// reverse a reversal that is still in progress, they'll incrementally speed up.
if (duration < kAnimationDuration)
lastAnimationStart -= (kAnimationDuration - duration);
// grab the state of the layer as it is right now
CALayer *currentLayer = self.subview.layer.presentationLayer;
// cancel any animations in progress
[self.subview.layer removeAllAnimations];
// set the transform to be as it is now, possibly in the middle of an animation
self.subview.layer.transform = currentLayer.transform;
// toggle our flag as to whether we're looking at large view or not
self.large = !self.large;
// set the transform based upon the state of the `large` boolean
CATransform3D newTransform;
if (self.large)
newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0);
else
newTransform = CATransform3DIdentity;
// now animate to our new setting
[UIView animateWithDuration:duration
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.layer.transform = newTransform;
}
completion:NULL];
}