Вопрос

Можно ли отменить UIView анимация во время ее выполнения?Или мне придется опуститься до уровня CA?

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

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

// set view properties

[UIView commitAnimations];

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

Это было полезно?

Решение

Я делаю это так: создаю новую анимацию для вашей конечной точки.Установите очень короткую продолжительность и обязательно используйте +setAnimationBeginsFromCurrentState: метод, позволяющий начать с текущего состояния.Если вы установите значение ДА, текущая анимация будет обрезана.Выглядит примерно так:

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

// set view properties

[UIView commitAnimations];

Другие советы

Использовать:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

Самый простой способ остановиться все анимация для определенного вида, немедленно, это:

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

#import <QuartzCore/QuartzCore.h>

Теперь, когда вы хотите остановить всю анимацию в представлении, застывшую на месте, скажите следующее:

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

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

На 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;
                     }
                 }];
}

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

Извините, что воскрешаю этот ответ, но в 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()

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

Если вы анимируете ограничение, изменяя константу вместо свойства представления, ни один из других методов не работает в 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];
}

Если вы просто хотите плавно приостановить/остановить анимацию

self.yourView.layer.speed = 0;

Источник: Как приостановить анимацию дерева слоев

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

Я решил свою проблему: «Я хочу анимировать то место, где вы выглядите так, как вы выглядите» («вы» означает вид).Если вы хотите ЭТОГО, то:

  1. Добавьте КварцКор.
  2. 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) {}];

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

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

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

+ (void)setAnimationsEnabled:(BOOL)enabled

отключает ВСЕ анимации и поэтому у меня не работает.Есть два решения:

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

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) заключается в том, что всегда происходит задержка остановки анимации по завершении текущего цикла анимации.

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

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

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

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

// set view properties
UIView.commitAnimations()
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];

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

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

Когда я работаю с 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)
}

Ни одно из ответных решений мне не помогло.Я решил свои проблемы таким образом (не знаю, правильно ли это?), потому что у меня были проблемы при слишком быстром вызове (когда предыдущая анимация еще не была закончена).Я передаю желаемую анимацию с помощью 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()
                }
        })

    }

}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top