2014-02-01 1 views
6

Я пытаюсь выполнить эту основную UIView анимации при получении нажатия кнопки:UIViewAnimationOptionBeginFromCurrentState неожиданного поведения с основной анимацией

- (IBAction)buttonPress:(id)sender 
{ 
    self.sampleView.alpha = 0.0; 
    [UIView animateWithDuration:2.0 
          delay:0.0 
         options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState 
        animations:^{self.sampleView.alpha = 1.0;} 
        completion:NULL]; 
} 

До нажатия кнопки вид виден и имеет альфа-значение 1,0. Когда я отправляю действие кнопки, я ожидаю, что вид будет исчезать от альфа = 0.0 до альфа 1.0 в течение 2 секунд, но этого не произойдет. Когда я удаляю UIViewAnimationOptionBeginFromCurrentState, анимация отлично работает.

Похоже, что с набором опций UIViewAnimationOptionBeginFromCurrentState альфа = 0.0 не устанавливается, и анимация пропускается, поскольку она думает, что она уже равна 1.0.

Я пытаюсь понять, почему это будет происходить, так как компания Apple документации говорится, что UIViewAnimationOptionBeginFromCurrentState не имеет никакого эффекта, если другая анимация не выполняется:

"UIViewAnimationOptionBeginFromCurrentState Запустите анимацию из текущей настройки, связанной с уже анимация в полете. Если этого ключа нет, любые анимации в полете можно закончить до начала новой анимации. Если другая анимация не находится в полете, этот ключ не действует ».

+0

Это совершенно сбивает с толку. Я мог только догадываться, что это связано с фактическим состоянием вашего альфа-значения UIView. Он начинается с 1.0, что является полным значением. Несмотря на то, что яблоко утверждает, что оно игнорирует его, есть ли у вас какие-то другие анимации? – user2277872

+0

Да, я полагаю, что во время начала анимации текущее значение альфа все равно 1.0, потому что 0.0 не успел обновиться на экране. Поскольку анимированное значение равно 1.0, а текущее значение экрана равно 1.0, оно, вероятно, видит его без изменений и анимации не происходит. –

+0

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

ответ

3

Оказалось, что использование UIViewAnimationOptionBeginFromCurrentState не всегда работает должным образом.

Посмотрите на этот пример:

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 

    UIButton *theButton = [UIButton new]; 
    [self.view addSubview:theButton]; 
    theButton.frame = self.view.frame; 
    theButton.backgroundColor = [UIColor redColor]; 
    theButton.alpha = 0.05; 
    [theButton addTarget:self action:@selector(actionPressed:) forControlEvents:UIControlEventTouchUpInside]; 
} 

- (void)actionPressed:(UIButton *)theButton 
{ 
    theButton.alpha = 0.6; 
    [UIView animateWithDuration:5 
          delay:0 
         options:UIViewAnimationOptionBeginFromCurrentState 
        animations:^ 
    { 
     // you expect to animate from 0.6 to 1. but it animates from 0.05 to 1 
     theButton.alpha = 1; 
    } 
        completion:nil]; 
} 

В приведенном выше примере вы ожидаете, что .alpha будет анимировать от 0,6 до 1. Однако, она одушевляет от 0,05 до 1.

Для того, чтобы решить эту проблему , вы должны изменить actionPressed: на следующее:

- (void)actionPressed:(UIButton *)theButton 
{ 
    [UIView animateWithDuration:0 
        animations:^ 
    { 
     // set new values INSIDE of this block, so that changes are 
     // captured in UIViewAnimationOptionBeginFromCurrentState. 
     theButton.alpha = 0.6; 
    } 
        completion:^(BOOL finished) 
    { 
     [UIView animateWithDuration:5 
           delay:0 
          options:UIViewAnimationOptionBeginFromCurrentState 
          animations:^ 
      { 
       // now it really animates from 0.6 to 1 
       theButton.alpha = 1; 
      } 
          completion:nil]; 
    }]; 
} 

Упоминание animateWithDuration:0 !!!

Правило простое: используйте только анимационный блок с UIViewAnimationOptionBeginFromCurrentState ПОСЛЕ некоторого другого блока анимации, чтобы на самом деле были применены все ваши предыдущие изменения.

В случае, если вы не знаете, что именно должно быть включено в animateWithDuration:0 блок, то вы можете использовать этот трюк:

- (void)actionPressed:(UIButton *)theButton 
{ 
    // make all your changes outside of the animation block 
    theButton.alpha = 0.6; 

    // create a fake view and add some animation to it. 
    UIView *theFakeView = [UIView new]; 
    theFakeView.alpha = 1; 
    [UIView animateWithDuration:0 
        animations:^ 
    { 
     // we need this line so that all previous changes are ACTUALLY applied 
     theFakeView.alpha = 0; 
    } 
        completion:^(BOOL finished) 
    { 
     [UIView animateWithDuration:5 
           delay:0 
          options:UIViewAnimationOptionBeginFromCurrentState 
          animations:^ 
      { 
       // now it really animates from 0.6 to 1 
       theButton.alpha = 1; 
      } 
          completion:nil]; 
    }]; 
} 

Если вы не хотите, чтобы помнить обо всех деталей эту ошибку, то просто примените Method Swizzling к классу UIView.

ВАЖНО EDIT: Оказывается, что приведенный ниже код вызовет сбой во время выполнения, если вы звоните performBatchUpdates:completion: из UICollectionView экземпляра. Поэтому в этом случае я не рекомендую использовать method swizzling!

Ваш код может выглядеть следующим образом:

+ (void)load 
{ 
    static dispatch_once_t theOnceToken; 
    dispatch_once(&theOnceToken,^
        { 
         Class theClass = object_getClass(self); 
         SEL theOriginalSelector = @selector(animateWithDuration:delay:options:animations:completion:); 
         SEL theSwizzledSelector = @selector(swizzled_animateWithDuration:delay:options:animations:completion:); 
         Method theOriginalMethod = class_getClassMethod(theClass, theOriginalSelector); 
         Method theSwizzledMethod = class_getClassMethod(theClass, theSwizzledSelector); 

         if (!theClass ||!theOriginalSelector || !theSwizzledSelector || !theOriginalMethod || !theSwizzledMethod) 
         { 
          abort(); 
         } 

         BOOL didAddMethod = class_addMethod(theClass, 
                  theOriginalSelector, 
                  method_getImplementation(theSwizzledMethod), 
                  method_getTypeEncoding(theSwizzledMethod)); 

         if (didAddMethod) 
         { 
          class_replaceMethod(theClass, 
               theSwizzledSelector, 
               method_getImplementation(theOriginalMethod), 
               method_getTypeEncoding(theOriginalMethod)); 
         } 
         else 
         { 
          method_exchangeImplementations(theOriginalMethod, theSwizzledMethod); 
         } 
        }); 
} 

+ (void)swizzled_animateWithDuration:(NSTimeInterval)duration 
           delay:(NSTimeInterval)delay 
          options:(UIViewAnimationOptions)options 
          animations:(void (^)(void))animations 
          completion:(void (^)(BOOL))completion 
{ 
    if (options & UIViewAnimationOptionBeginFromCurrentState) 
    { 
     UIView *theView = [UIView new]; 
     theView.alpha = 1; 
     [UIView animateWithDuration:0 
         animations:^ 
     { 
      theView.alpha = 0; 
     } 
         completion:^(BOOL finished) 
     { 
      [self swizzled_animateWithDuration:duration 
              delay:delay 
             options:options 
            animations:animations 
            completion:completion]; 
     }]; 
    } 
    else 
    { 
     [self swizzled_animateWithDuration:duration 
            delay:delay 
            options:options 
           animations:animations 
           completion:completion]; 
    } 
} 

Если добавить этот код в вашей категории пользовательских UIView, то этот код будет работать хорошо сейчас:

- (void)actionPressed:(UIButton *)theButton 
{ 
    theButton.alpha = 0.6; 
    [UIView animateWithDuration:5 
          delay:0 
         options:UIViewAnimationOptionBeginFromCurrentState 
        animations:^ 
    { 
     // you expect to animate from 0.6 to 1. 
     // it will do so ONLY if you add the above code sample to your project. 
     theButton.alpha = 1; 
    } 
        completion:nil]; 
} 

В моем случае, это ошибка полностью разрушил мои анимации. Смотрите ниже:

[Demo CountPages alpha] [Demo CountPages alpha]

1

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

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

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

- (IBAction)buttonPressed:(id)sender 
{ 
    self.sampleView.alpha = 0.0; 

    [self performSelector:@selector(animateAlpha) withObject:nil afterDelay:0.1]; 
} 

- (void)animateAlpha 
{ 
    [UIView animateWithDuration:2.0 
         delay:0.0 
         options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState 
        animations:^{self.sampleView.alpha = 1.0;} 
        completion:NULL]; 
} 
0

При использовании UIViewAnimationOptionBeginFromCurrentState вместо performSelector: Мне нравится устанавливать начальные значения UIViewperformWithoutAnimation прямо перед вызовом animateWithDuration:.

 Смежные вопросы

  • Нет связанных вопросов^_^