Отменить анимацию UIView?

Можно ли отменить анимацию UIView во время ее выполнения? Или мне нужно будет перейти на уровень СА?

то есть. Я сделал что-то вроде этого (возможно, также установил действие анимации конца):

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

Но прежде чем анимация завершится, и я получу событие, закончившее анимацию, я хочу отменить его (сократите его). Это возможно? Гуглинг вокруг обнаруживает, что несколько человек задают один и тот же вопрос без ответов - и один или два человека предполагают, что это невозможно.

Ответ 1

То, как я это делаю, - создать новую анимацию до конечной точки. Установите очень короткую продолжительность и убедитесь, что вы используете метод +setAnimationBeginsFromCurrentState:, чтобы начать с текущего состояния. Когда вы установите значение YES, текущая анимация будет прервана. Выглядит примерно так:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

Ответ 2

Использование:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

Ответ 3

Самый простой способ остановить все анимации на определенном представлении сразу:

Свяжите проект с QuartzCore.framework. В начале вашего кода:

#import <QuartzCore/QuartzCore.h>

Теперь, когда вы хотите остановить все анимации на мертвом месте, скажите это:

[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];

Средняя линия будет работать сама по себе, но там задержка до тех пор, пока runloop не закончится ( "момент перерисовки" ). Чтобы предотвратить эту задержку, оберните команду в явный блок транзакций, как показано. Это работает, при этом никаких других изменений в этом слое в текущей runloop не выполнялось.

Ответ 4

В iOS 4 и выше используйте опцию UIViewAnimationOptionBeginFromCurrentState во второй анимации, чтобы сократить первую анимацию.

В качестве примера предположим, что у вас есть представление с индикатором активности. Вы хотите исчезнуть в индикаторе активности, пока начинается какое-то потенциально продолжительное время, и исчезает, когда деятельность завершена. В приведенном ниже коде представление с индикатором активности на нем называется activityView.

- (void)showActivityIndicator {
    activityView.alpha = 0.0;
    activityView.hidden = NO;
    [UIView animateWithDuration:0.5
                 animations:^(void) {
                     activityView.alpha = 1.0;
                 }];

- (void)hideActivityIndicator {
    [UIView animateWithDuration:0.5
                 delay:0 options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^(void) {
                     activityView.alpha = 0.0;
                 }
                 completion:^(BOOL completed) {
                     if (completed) {
                         activityView.hidden = YES;
                     }
                 }];
}

Ответ 5

Чтобы отменить анимацию, вам просто нужно установить свойство, которое в настоящее время анимируется, вне анимации UIView. Это остановит анимацию везде, где она есть, и UIView перейдет к настройке, которую вы только что определили.

Ответ 6

Извините, что воскресил этот ответ, но в iOS 10 все изменилось, и теперь отмена возможна, и вы даже можете отменить изящно!

После iOS 10 вы можете отменить анимацию с UIViewPropertyAnimator !

UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
    view.backgroundColor = .blue
})
animator.stopAnimation(true)

Если вы передадите true, анимация будет отменена и остановится там, где вы ее отменили. Метод завершения не будет вызван. Однако, если вы передадите false, вы несете ответственность за завершение анимации:

animator.finishAnimation(.start)

Вы можете закончить анимацию и оставаться в текущем состоянии (.current) или перейти в исходное состояние (.start) или в конечное состояние (.end)

Кстати, вы можете даже приостановить и перезагрузить позже...

animator.pauseAnimation()
animator.startAnimation()

Примечание. Если вы не хотите внезапного отмены, вы можете полностью изменить анимацию или даже изменить ее после приостановки!

Ответ 7

Если вы анимируете ограничение, изменяя константу вместо свойства вида, ни один из других методов не работает на iOS 8.

Пример анимации:

self.constraint.constant = 0;
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.0f
                      delay:0.0f
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     self.constraint.constant = 1.0f;
                     [self.view layoutIfNeeded];
                 } completion:^(BOOL finished) {

                 }];

Решение:

Вам нужно удалить анимации из слоев любых видов, на которые влияет изменение ограничения и их подслоев.

[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
    [l removeAllAnimations];
}

Ответ 9

Ни один из вышеперечисленных не решил это для меня, но это помогло: анимация UIView сразу устанавливает свойство, а затем анимирует его. Он останавливает анимацию, когда уровень представления соответствует модели (заданное свойство).

Я решил свою проблему, которая была "Я хочу оживить, откуда вы выглядите, как будто вы появились" ( "вы" означает представление). Если вы хотите THAT, то:

  • Добавить QuartzCore.
  • CALayer * pLayer = theView.layer.presentationLayer;

установить положение слоя представления

Я использую несколько опций, включая UIViewAnimationOptionOverrideInheritedDuration

Но поскольку документация Apple неясна, я не знаю, действительно ли она переопределяет другие анимации при ее использовании или просто сбрасывает таймеры.

[UIView animateWithDuration:blah... 
                    options: UIViewAnimationOptionBeginFromCurrentState ... 
                    animations: ^ {
                                   theView.center = CGPointMake( pLayer.position.x + YOUR_ANIMATION_OFFSET, pLayer.position.y + ANOTHER_ANIMATION_OFFSET);
                   //this only works for translating, but you get the idea if you wanna flip and scale it. 
                   } completion: ^(BOOL complete) {}];

И это должно быть достойное решение на данный момент.

Ответ 10

[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];

Ответ 11

У меня та же проблема; API не имеют ничего, чтобы отменить определенную анимацию.

+ (void)setAnimationsEnabled:(BOOL)enabled

отключает ВСЕ анимации и, таким образом, не работает для меня. Там два решения:

1) сделайте ваш анимированный объект подвью. Затем, когда вы хотите отменить анимацию для этого вида, удалите представление или скройте его. Очень просто, но вам нужно воссоздать subview без анимации, если вам нужно держать его в поле зрения.

2) повторите анимацию только один, и сделайте селектор делегатов для повторного запуска анимации, если это необходимо, например:

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
    [self startAnimation];
} else {
    self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
    [self startAnimation];
}
}

Проблема с 2) заключается в том, что всегда останавливается остановка анимации по мере завершения текущего цикла анимации.

Надеюсь, что это поможет.

Ответ 12

Даже если вы отмените анимацию в способах выше анимации didStopSelector, все еще выполняется. Поэтому, если в вашем приложении есть логические состояния, связанные с анимацией, у вас будут проблемы. По этой причине описанными выше способами я использую контекстную переменную анимации UIView. Если вы передадите текущее состояние вашей программы по параметру контекста анимации, когда анимация остановится, функция didStopSelector может решить, должна ли она что-то сделать или просто вернуться на основе текущего состояния и значения состояния, переданного как контекст.

Ответ 13

Свифт версия решения Стивена Дарлингтона

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties

// set view properties
UIView.commitAnimations()

Ответ 14

CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];

Ответ 15

Чтобы приостановить анимацию без возврата к исходному или окончательному:

CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;

Ответ 16

Когда я работаю с анимацией UIStackView, кроме removeAllAnimations() мне нужно установить некоторые значения в исходное, потому что removeAllAnimations() может установить их в непредсказуемое состояние. У меня есть stackView с view1 и view2 внутри, и один вид должен быть видимым, а другой скрытым:

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}

Ответ 17

Ни одно из ответов не сработало для меня. Я решил свои проблемы таким образом (я не знаю, является ли это правильным способом?), Потому что у меня были проблемы при вызове этого слишком быстро (когда предыдущая анимация еще не была закончена). Я передаю желаемую анимацию с помощью блока customAnim.

extension UIView
{

    func niceCustomTranstion(
        duration: CGFloat = 0.3,
        options: UIView.AnimationOptions = .transitionCrossDissolve,
        customAnim: @escaping () -> Void
        )
    {
        UIView.transition(
            with: self,
            duration: TimeInterval(duration),
            options: options,
            animations: {
                customAnim()
        },
            completion: { (finished) in
                if !finished
                {
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    self.layer.removeAllAnimations()
                    customAnim()
                }
        })

    }

}