UIViewAnimationOptionBeginFromCurrentState基本动画的意外行为 [英] UIViewAnimationOptionBeginFromCurrentState unexpected behaviour with basic animation

查看:1281
本文介绍了UIViewAnimationOptionBeginFromCurrentState基本动画的意外行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在收到按钮点击时执行这个基本的 UIView 动画:

I am trying to perform this basic UIView animation upon receiving a button click:

- (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];
}

在按钮之前单击视图是可见的,并且alpha值为1.0 。当我发送按钮动作时,我期望视图从alpha = 0.0到alpha = 1.0在2秒内淡入,但这不会发生。当我删除 UIViewAnimationOptionBeginFromCurrentState 的动画工作正常。

Prior to the button click the view is visible and has an alpha value of 1.0. When I send the button action I expect the view to fade in from alpha=0.0 to alpha=1.0 over 2 seconds but this does not happen. When I remove the UIViewAnimationOptionBeginFromCurrentState the animation works fine.

看起来像使用 UIViewAnimationOptionBeginFromCurrentState 选项集,alpha = 0.0未设置,因为它认为它已经在1.0。

It seems like with the UIViewAnimationOptionBeginFromCurrentState option set, the alpha=0.0 is not being set and the animation is skipped because it thinks it is already at 1.0.

我试图理解为什么会发生,因为苹果文档说明 UIViewAnimationOptionBeginFromCurrentState 无效:

I am trying to understand why this would be happening since Apple documentation states that the UIViewAnimationOptionBeginFromCurrentState has no effect if another animation is not in progress:

UIViewAnimationOptionBeginFromCurrentState
如果另一个动画不在飞行中,则此键不具有任何动画,如果另一个动画不在飞行中,则该键不具有效果。

"UIViewAnimationOptionBeginFromCurrentState Start the animation from the current setting associated with an already in-flight animation. If this key is not present, any in-flight animations are allowed to finish before the new animation is started. If another animation is not in flight, this key has no effect."

推荐答案

事实证明,使用 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.

In the example above you expect that .alpha would animate from 0.6 to 1. However, it animates from 0.05 to 1.

为了解决这个问题,你应该更改 actionPressed:

In order to resolve the issue, you should change actionPressed: to the following:

- (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

The rule is easy: Only use an animation block with UIViewAnimationOptionBeginFromCurrentState AFTER some other animation block, so that all your previous changes are actually applied.

如果您不知道应该在<$ c $中包含什么内容c> animateWithDuration:0 阻止,那么你可以使用这个技巧:

In case you don't know what exactly should be included in animateWithDuration:0 block, then you can use this trick:

- (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类。


If you don't want to remember about all details of this bug, then just apply Method Swizzling to UIView class.

重要编辑:原因是下面的代码会在运行时导致崩溃,如果调用 performBatchUpdates:completion: c> UICollectionView 实例。所以,我不建议在这种情况下使用方法swizzling

IMPORTANT It turns out that the code below will cause a crash at runtime, if calling performBatchUpdates:completion: of UICollectionView instance. So, I do not recommend using method swizzling in this case!

您的代码可能如下所示:

Your code may look like this:

+ (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类别,那么这段代码现在可以正常工作了:

If you add this code to your custom UIView category, then this code would work fine now:

- (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];
}



在我的例子中,这个错误完全毁了我的动画。请参阅下文:

In my case, this bug has completely ruined my animations. See below:

[]
[]

[] []

这篇关于UIViewAnimationOptionBeginFromCurrentState基本动画的意外行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆