Подтвердить что ты не робот

Как остановить и обратить вспять анимацию UIView?

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

В Apple Q & Как я нашел способ немедленно приостановить все анимации:

https://developer.apple.com/library/ios/#qa/qa2009/qa1673.html

Но я не вижу способа отменить анимацию отсюда (и опустить оставшуюся часть начальной анимации). Как это сделать?

- (IBAction)toggleMeter:(id)sender {
    if (self.myView.hidden) {        
        self.myView.hidden = NO;
        [UIView animateWithDuration:3 animations:^{
            self.myView.transform = expandMatrix;
        } completion:nil];
    } else {
        [UIView animateWithDuration:3 animations:^{
            self.myView.transform = shrinkMatrix;
        } completion:^(BOOL finished) {
            self.myView.hidden = YES;
        }];
    }
}
4b9b3361

Ответ 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];
}

Ответ 2

Существует общий трюк, который вы можете использовать для этого, но необходимо написать отдельный метод для сжатия (и другого подобного для расширения):

- (void) shrink {  
    [UIView animateWithDuration:0.3
                     animations:^{
                        self.myView.transform = shrinkALittleBitMatrix;
                     }
                     completion:^(BOOL finished){
                          if (continueShrinking && size>0) {
                              size=size-1;
                              [self shrink];      
                           }
                     }];
}

Итак, теперь трюк состоит в том, чтобы сломать 3-секундную анимацию сокращения до 10 анимаций (или более 10, конечно) по 0,3 сек каждая, в которой вы сокращаете 1/10 всей анимации: shrinkALittleBitMatrix. После завершения каждой анимации вы вызываете тот же метод только тогда, когда bool ivar continueShrinking является истинным, и когда int ivar size является положительным (представление в полном размере будет size = 10, а представление с минимальным размером будет размером = 0). Когда вы нажимаете кнопку, вы меняете ivar continueShrinking на FALSE, а затем вызываете expand. Это остановит анимацию менее чем за 0,3 секунды.

Ну, вы должны заполнить детали, но я надеюсь, что это поможет.

Ответ 3

Первый: как удалить или отменить анимацию с представлением?

[view.layer removeAllAnimations] 

Если в представлении много анимаций, например, одна анимация перемещается сверху вниз, другая перемещается слева направо;

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

[view.layer removeAnimationForKey:@"someKey"];
// the key is you assign when you create a animation 
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"someKey"]; 

когда вы это сделаете, анимация остановится, он вызовет делегат:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

если флаг == 1, укажите, что анимация завершена. if flag == 0, укажите, что анимация не завершена, она может быть отменена, удалена.

Второй:, поэтому вы можете делать то, что хотите сделать в этом делетете.

если вы хотите получить рамку представления при удалении кода, вы можете сделать это:

currentFrame = view.layer.presentationlayer.frame;

Примечание:

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

Я не могу сейчас решить этот вопрос. если когда-нибудь я смогу, я обновлю этот вопрос.