Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/109.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios UIViewAnimationOptionBeginFromCurrentState基本动画的意外行为_Ios_Objective C_Cocoa Touch_Uiviewanimation - Fatal编程技术网

Ios UIViewAnimationOptionBeginFromCurrentState基本动画的意外行为

Ios UIViewAnimationOptionBeginFromCurrentState基本动画的意外行为,ios,objective-c,cocoa-touch,uiviewanimation,Ios,Objective C,Cocoa Touch,Uiviewanimation,我正在尝试在收到按钮单击后执行此基本ui视图动画: - (IBAction)buttonPress:(id)sender { self.sampleView.alpha = 0.0; [UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimat

我正在尝试在收到按钮单击后执行此基本
ui视图
动画:

- (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。当我发送按钮动作时,我希望视图在2秒内从alpha=0.0淡入alpha=1.0,但这不会发生。当我删除
UIViewAnimationOptionBeginFromCurrentState
时,动画工作正常

似乎设置了
UIViewAnimationOptionBeginFromCurrentState
选项后,alpha=0.0没有被设置,动画被跳过,因为它认为它已经处于1.0

我正在试图理解为什么会发生这种情况,因为苹果的文档说明,如果没有其他动画进行,则
UIViewAnimationOptionBeginFromCurrentState
无效:

UIViewAnimationOptionBeginFromCurrentState

从与已在运行中的动画相关联的当前设置启动动画。如果此键不存在,则允许在启动新动画之前完成任何在运行中的动画。如果另一个动画不在运行中,则此键无效。“

为了查看发生了什么,为帧位置而不是alpha设置动画很有帮助。如果执行此操作,请尝试每隔一秒左右用
UIViewAnimationOptionBeginFromCurrentState
设置点击按钮,您将开始看到一些移动

将alpha设置为零时,直到下一次通过运行循环时,才会更新显示。由于随后立即启动动画,因此显示没有机会更新,当动画框架启动时,它会查看当前显示的alpha(1.0)并查看其必须到达的位置(alpha为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];
}

当使用
UIViewAnimationOptionBeginFromCurrentState
而不是
PerformSelect:
时,我喜欢在调用
animateWithDuration:
之前,使用
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];
     }];
}
提及持续时间为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];
     }];
}

如果您不想记住这个bug的所有细节,那么只需应用到UIView类

重要编辑:如果调用
UICollectionView
实例的
performbatchUpdate:completion:
,下面的代码将在运行时导致崩溃。因此,我不建议在这种情况下使用
方法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];
}
在我的例子中,这个bug完全破坏了我的动画。见下文:

[]
[]

这太令人困惑了。我只能猜测它与UIView的alpha值的实际状态有关。它从1.0开始,这是完整值。即使苹果公司声明它忽略了它,你在它下面还有其他动画吗?是的,我最好的猜测是,在动画开始时,当前的alpha值仍然是1.0,因为0.0还没有机会在屏幕上更新。因为动画值是1.0,而当前屏幕上的值也是1.0,所以它可能认为没有变化,也没有动画出现。我同意这一点。我想既然你的动画看起来很小,你就不需要使用当前状态部分了。看看去掉它是否会改变任何东西去掉UIViewAnimationOptionBeginFromCurrentState肯定能解决我所看到的问题(参见原始帖子)。但是,在我的应用程序中,我打算使用此选项,因为我希望我的动画偶尔会被另一个动画打断,并且我希望从一个动画平滑过渡到另一个动画。我想知道为什么包含它会产生不同的行为。