带约束的动画立即完成

时间:2013-10-29 18:17:00

标签: ios core-animation nslayoutconstraint

我有一些动画我试图转换为约束。其背后的想法是,当出现错误时,登录提示会反弹。

我的方法是视图控制器上的所有内容都在bounceView中,它本身位于主视图中。

bounceView使用居中约束和前导空间约束绑定到主视图。为了使设计在设计时明确无误,还有一个领先的空间和顶部空间约束。但这些只是占位符,并且在运行时被替换为相同的高度/宽度约束。

没有其他约束与主视图相关联。

现在,一些细节......

我使用这些约束来定义反弹:

enum {animationDone, bounceStart, bounceLeft, bounceRight, bounceReturn};
static const NSTimeInterval BounceDuration = 20.0;
static const int NumberOfBounces = 3;
static const CGFloat BounceDistance = 16.0f;

(20秒延迟通常要短得多;我试图证明这不起作用。)

额外的运行时约束设置如下:

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_bounceView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_bounceView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
}

我用这段代码开始动画:

_animateCount = NumberOfBounces;
_animateStage = bounceStart;
[self animationStep];

实际代码非常简单:

- (void)animationStep {
    CGFloat bounceTo = 0;
    BOOL half = NO;
    switch (_animateStage) {
        case bounceStart:
            _animateStage = bounceLeft;
            bounceTo = -BounceDistance;
            half = YES;
            break;
        case bounceLeft:
            _animateStage = bounceRight;
            bounceTo = BounceDistance;
            break;
        case bounceRight:
            if ( --_animateCount > 0 ) {
                _animateStage = bounceLeft;
                bounceTo = -BounceDistance;
            } else {
                _animateStage = bounceReturn;
                bounceTo = 0;
            }
            break;
        case bounceReturn:
            half = YES;
            _animateStage = animationDone;
            break;
    }

    BOOL finishedAnimation = ( ( _animateStage == animationDone ) || ( self.view == nil ) );

    if ( !finishedAnimation ) {
        [_bounceView layoutIfNeeded];
        _centerX.constant = bounceTo;
        [_bounceView setNeedsUpdateConstraints];

        NSTimeInterval duration = half ? BounceDuration / 2.0f : BounceDuration;
        [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            NSLog(@"Bouncing to %f over %f seconds.", bounceTo, duration);
            [_bounceView layoutIfNeeded];
        } completion:^(BOOL finished) {
            [self animationStep];
        }];
    }
}

然而,这一切都是立即完成的:

2013-10-29 10:59:43.852 App[9151:907] Bouncing to -16.000000 over 20.000000 seconds.
2013-10-29 10:59:43.864 App[9151:907] Bouncing to 16.000000 over 20.000000 seconds.
2013-10-29 10:59:43.865 App[9151:907] Bouncing to -16.000000 over 20.000000 seconds.
2013-10-29 10:59:43.866 App[9151:907] Bouncing to 16.000000 over 20.000000 seconds.
2013-10-29 10:59:43.866 App[9151:907] Bouncing to -16.000000 over 20.000000 seconds.
2013-10-29 10:59:43.867 App[9151:907] Bouncing to 16.000000 over 20.000000 seconds.
2013-10-29 10:59:43.867 App[9151:907] Bouncing to 0.000000 over 20.000000 seconds.

有什么想法吗?

更新

对于后代,这是通过在子视图上调用setNeedsUpdateConstraintslayoutIfNeeded引起的。尽管Apple的文档另有说明,但您需要在superview上调用它。我在下面的答案中对此进行了详细说明。

然而,我已经接受了@ nielsbot的回答。尽管不是我问题的直接答案,但这比解决我想要做的事情更好。

3 个答案:

答案 0 :(得分:1)

您是否无法使用核心动画临时为视图设置动画?

请在此处查看我的回答:https://stackoverflow.com/a/9371196/210171

基本上,在要设置动画的视图上,获取其图层并向其添加动画。如果通过为图层的变换设置动画来执行此操作,则不会更改视图的大小或位置,也不会触发布局循环。

CAKeyframeAnimation * anim = [ CAKeyframeAnimation animationWithKeyPath:@"transform" ] ;
anim.values = @[ [ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-5.0f, 0.0f, 0.0f) ], [ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(5.0f, 0.0f, 0.0f) ] ] ;
anim.autoreverses = YES ;
anim.repeatCount = 2.0f ;
anim.duration = 0.07f ;

[ viewToShake.layer addAnimation:anim forKey:nil ] ;

答案 1 :(得分:0)

pattern suggested in this post是否有帮助?

我知道您对动画使用的细粒度控制方法比该帖子中的简单控制方法要好,但我认为基础setNeedsUpdateLayoutConstraints和后续layoutIfNeeded调用仍适用于此处。

答案 2 :(得分:0)

您必须在超级视图上同时调用setNeedsUpdateConstraintslayoutIfNeeded。尽管Apple的文档说layoutIfNeeded会抓取视图,但情况似乎并非如此。

以下参考,Apple目前的layoutIfNeeded文档:

  

使用此方法在绘制前强制子视图的布局。从接收者开始,只要superviews需要布局,此方法就会向上遍历视图层次结构。然后它展示了祖先下面的整棵树。因此,调用此方法可能会强制整个视图层次结构的布局。