UIView 动画未在值更改事件上设置动画

Posted

技术标签:

【中文标题】UIView 动画未在值更改事件上设置动画【英文标题】:UIView Animations Not Animating on Value Changed Event 【发布时间】:2012-10-22 15:01:33 【问题描述】:

我正在尝试根据 DCRoundSwitch 对象的值隐藏或显示视图,但 UIView 动画块没有动画 - 更改会立即应用。我还尝试使用 CATransaction 为视图层设置动画,但这些更改也立即应用了。如果将同一个动画块移动到另一个位置(例如 viewWillAppear:),动画效果很好。

非常感谢任何帮助确定动画无法正常工作的原因。谢谢!

这是动画代码:

- (void)switchValueChanged:(id)sender

    [UIView animateWithDuration:0.33
                     animations:^
                         self.containerView.alpha = self.switchView.isOn ? 1 : 0;
                     ];

作为参考,这里是 DCRoundSwitch 用于更改开关值的代码。 animatedYESignoreControlEventsNO

- (void)setOn:(BOOL)newOn animated:(BOOL)animated ignoreControlEvents:(BOOL)ignoreControlEvents

    BOOL previousOn = self.on;
    on = newOn;
    ignoreTap = YES;

    [CATransaction setAnimationDuration:0.014];
    knobLayer.gripped = YES;

    // setup by turning off the manual clipping of the toggleLayer and setting up a layer mask.
    [self useLayerMasking];
    [self positionLayersAndMask];

    [CATransaction setCompletionBlock:^
        [CATransaction begin];
        if (!animated)
            [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
        else
            [CATransaction setValue:(id)kCFBooleanFalse forKey:kCATransactionDisableActions];

        CGFloat minToggleX = -toggleLayer.frame.size.width / 2.0 + toggleLayer.frame.size.height / 2.0;
        CGFloat maxToggleX = -1;


        if (self.on)
        
            toggleLayer.frame = CGRectMake(maxToggleX,
                                           toggleLayer.frame.origin.y,
                                           toggleLayer.frame.size.width,
                                           toggleLayer.frame.size.height);
        
        else
        
            toggleLayer.frame = CGRectMake(minToggleX,
                                           toggleLayer.frame.origin.y,
                                           toggleLayer.frame.size.width,
                                           toggleLayer.frame.size.height);
        

        if (!toggleLayer.mask)
        
            [self useLayerMasking];
            [toggleLayer setNeedsDisplay];
        

        [self positionLayersAndMask];

        knobLayer.gripped = NO;

        [CATransaction setCompletionBlock:^
            [self removeLayerMask];
            ignoreTap = NO;

            // send the action here so it get's sent at the end of the animations
            if (previousOn != on && !ignoreControlEvents)
                [self sendActionsForControlEvents:UIControlEventValueChanged];
        ];

        [CATransaction commit];
    ];

【问题讨论】:

另外:如果我用 UISwitch 替换 DCRoundSwitch,动画就会起作用,所以它必须与开关的动画块有关。有谁知道CATransaction 有什么问题吗? 在有人问之前,我不能在最终产品中使用UISwitch,因为DCRoundSwitch 提供了UISwitch 没有的自定义选项。 我看不到您是在初始事务中调用 [CATransaction begin] 还是提交。然而……更值得怀疑的是口罩的使用。从某种意义上说,您正在尝试为 alpha 值设置动画,同时弄乱蒙版,这是有问题的。由于我无法确切地看到你在用面具做什么,我不知道它是否会产生影响。一般来说(根据我的经验):如果您在动画块内(或在这种情况下与另一个动画块同时)对直接或间接影响属性(例如 alpha)的内容进行多项更改,... (cont) 该属性的初始状态可能会在动画开始之前被覆盖。例如,在动画块中多次设置视图的帧可能会导致动画的初始帧成为间歇帧之一。在您的情况下,您可能正在使用蒙版操作视觉 alpha,或者如果使用图层蒙版(内部)实现实际 alpha。如果可以的话,我会尝试将 alpha 动画的功能与 setOn 动画结合起来,使动画块之间的属性更改不重叠。 初始事务中所做的更改被添加到隐式事务中,这就是没有调用begincommit 的原因。图层/蒙版都是 DCRoundSwitch (github.com/domesticcatsoftware/DCRoundSwitch) 的一部分,它与容器共享父视图。对一个视图的掩码的更改不应该影响对同级的 alpha 的更改,不是吗? 【参考方案1】:

与@Matt 谈论隐式事务让我想到...... UIView 的动画方法环绕CATransaction,并且属性更改通常添加到隐式事务中。但是,没有为完成块中所做的更改创建事务,这显然会影响[UIView animateWithDuration]。我变了

if (previousOn != on && !ignoreControlEvents)
    [self sendActionsForControlEvents:UIControlEventValueChanged];

if (previousOn != on && !ignoreControlEvents)

    [CATransaction begin];
    [CATransaction setDisableActions:NO];
    [self sendActionsForControlEvents:UIControlEventValueChanged];
    [CATransaction commit];

它有效。 ([CATransaction setDisableActions:NO] 也是必要的)

感谢大家的帮助。

【讨论】:

太棒了!很高兴你明白了。【参考方案2】:

我不知道带有 CATransaction 的 DCRoundSwitch 代码中发生了什么以及它是如何工作的。 但你可以试试这个:

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.33];

self.containerView.alpha = self.switchView.isOn ? 1 : 0;

[UIView commitAnimations];

【讨论】:

这应该提供与使用动画块相同的行为,并且不推荐使用这种格式以支持动画块。一些默认值已经改变(动画曲线?),但没有什么会导致动画不工作/根据不同的实现工作。 感谢您的帖子,但@Matt 是正确的;这具有相同的结果。【参考方案3】:

我已经使用了上述奥斯汀解决方案。但无法修复。

我使用了下面的代码。哪个工作正常。

请关注本帖

https://github.com/domesticcatsoftware/DCRoundSwitch/issues/12

你可以去使用这个 DCRoundSwitch 的类,在那个类的 dealloc 方法上放这一行。

 - (void)dealloc
 
      [self.MyDCRoundSwitch removeTarget:nil
                         action:NULL
                         forControlEvents:UIControlEventAllEvents];
      [super dealloc];
 

【讨论】:

以上是关于UIView 动画未在值更改事件上设置动画的主要内容,如果未能解决你的问题,请参考以下文章

如何为嵌入在 UITableViewCell 中的 UIView 设置动画

使用Autolayout为UIView设置动画

UIView 使用自定义属性有条件地为子视图设置动画

在约束上动画更改 UITableView 节标题的高度

MonoTouch - UIView.Animate 完成回调未在按钮 touchUpInside 委托内调用

如何为 UIBezierPath 上的填充颜色变化设置动画?