0

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

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

使用居中约束和前导空间约束将bounceView 绑定到主视图。为了在设计时使布局明确,还有一个前导空间和顶部空间约束。不过,这些只是占位符,在运行时会替换为相等的高度/宽度约束。

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

现在,一些细节......</p>

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

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 的回答。尽管不是我问题的直接答案,但它比修复我试图做的事情更好。

4

3 回答 3

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 ] ;
于 2013-10-29T20:51:51.233 回答
0

这篇文章中建议的模式是否有帮助?

我了解您对动画使用的控制方法比该帖子中的简单方法更精细,但我认为底层setNeedsUpdateLayoutConstraints和后续layoutIfNeeded调用仍然适用于此。

于 2013-10-29T18:33:43.987 回答
0

您必须同时调用setNeedsUpdateConstraints和调用layoutIfNeededsuperview。尽管 Apple 的文档说layoutIfNeeded会爬上视图,但情况似乎并非如此。

供将来参考,Apple 当前的文档layoutIfNeeded

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

于 2013-10-29T20:42:15.363 回答