Оказалось, что использование 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];
}
В моем случае, это ошибка полностью разрушил мои анимации. Смотрите ниже:
[] []
Это совершенно сбивает с толку. Я мог только догадываться, что это связано с фактическим состоянием вашего альфа-значения UIView. Он начинается с 1.0, что является полным значением. Несмотря на то, что яблоко утверждает, что оно игнорирует его, есть ли у вас какие-то другие анимации? – user2277872
Да, я полагаю, что во время начала анимации текущее значение альфа все равно 1.0, потому что 0.0 не успел обновиться на экране. Поскольку анимированное значение равно 1.0, а текущее значение экрана равно 1.0, оно, вероятно, видит его без изменений и анимации не происходит. –
Я согласен с этим. Я думаю, так как ваши анимации кажутся настолько незначительными, вам не нужно будет использовать текущую часть состояния. Посмотрите, если это выйдет, оно что-то изменит – user2277872