当UIGravityBehavior处于活动状态时,UIDynamicAnimator拒绝达到平衡

时间:2013-10-15 15:20:48

标签: ios animation uikit-dynamics uidynamicbehavior

我正在研究可扩展托盘视图的代码,该视图使用UIDynamicAnimator来实现漂亮的扩展/合约动画。

为了获得逼真的加速度,我使用UIGravityBehavior让托盘掉落,直到托盘的“标签”碰到屏幕底部。

这很有效,但即使场景中的所有项目都停止移动,也永远不会调用UIDynamicAnimatorDelegate dynamicAnimatorDidPause:。这意味着动画师继续使用CPU周期为场景设置动画(委托已设置,并触发UIDynamicAnimatorDelegate dynamicAnimatorDidPause:)。

我尝试从场景中移除UIGravityBehavior,这确实导致动画师最终停止。我没有时间去除重力行为,因为我需要在一切都停止移动后将其从场景中移除。

我知道引力是一个恒定的力量,但我仍然认为一旦所有物体都有0速度和0加速度就会停止动画。

这最后的假设是假的吗?

有类似问题的人吗?

3 个答案:

答案 0 :(得分:6)

你知道,一旦一切都休息,动画师应该暂停。

检查items与重力行为的关联,并确保没有其他物品仍在下降。例如,很容易意外地创建以下错误:

  • 添加重力和碰撞视图
  • 从superview和碰撞中删除视图
  • 无法从重力中删除视图

在这种情况下,“鬼项目”将永远失效。

另一个可能的问题(尽管你的描述不太可能)是你的物品是否附加到导致无限但小的“反弹”的其他行为上。我会检查你的动画师的完整行为列表(记得检查孩子的行为)。特别是我对添加UIDynamicItemBehavior的任何elasticity感兴趣。


编辑:

你可能也想走另一条路。从一个非常基本的动力学系统开始,添加您的组件,直到您可以重现问题。例如,以下内容会很快收敛(记录“暂停”):

@interface PTLViewController () <UIDynamicAnimatorDelegate>
@property (nonatomic, strong) UIDynamicAnimator *animator;
@end

@implementation PTLViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100,100,100,100)];
  view.backgroundColor = [UIColor lightGrayColor];
  [self.view addSubview:view];

  self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
  self.animator.delegate = self;

  UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[view]];
  collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
  [self.animator addBehavior:collisionBehavior];

  UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[view]];
  [self.animator addBehavior:gravityBehavior];
}

- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator {
  NSLog(@"pause");
}
@end

关于获得所有物品速度的问题,我不知道一个简单的方法。遗憾的是,UIDynamicAnimator并未直接了解其所有项目。这是间接的,因为UIDyanamicBehavior不包含items属性。如果这对我造成困扰,请考虑重复radar://15054405

但是,如果您只想知道特定项目的当前线速度,则有一个解决方案。只需添加UIDynamicItemBehavior并使用自定义操作进行记录:

UIDynamicItemBehavior *dynamicItemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[view]];
__weak UIDynamicItemBehavior *weakBehavior = dynamicItemBehavior;
dynamicItemBehavior.action = ^{
  NSLog(@"Velocity: %@", NSStringFromCGPoint([weakBehavior linearVelocityForItem:view]));
};
[self.animator addBehavior:dynamicItemBehavior];

答案 1 :(得分:1)

我最近遇到过类似的问题。最后,我使用带有边界而不是项目的UICollisionBehavior(因为否则移动的项目会撞击其他项目......)并实现委托方法collisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint:以了解何时应该删除重力

UICollisionBehavior *collide = [[[UICollisionBehavior alloc] initWithItems:borders] retain];
[collide addItem:movingItem];

[collide setCollisionMode:UICollisionBehaviorModeBoundaries];
[collide setTranslatesReferenceBoundsIntoBoundary:YES];

如果您找到更好的解决方案,请告诉我们:)!

答案 2 :(得分:1)

我的问题是一样的。我的动画师永远不会休息所以一旦开始,我的应用程序永远消耗3到4%的CPU。我的观点似乎都在1/2秒内停止移动。因此,我不是弄清楚为什么我没有达到平衡,而是用锤子击打它并用计时器杀死动画师。我给它2秒钟。

- (void)createAnimator {
    if (_timer) {
        [_timer invalidate];
    }

    if (_animator) {
        [_animator removeAllBehaviors];
    }

    _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];

    // create all behaviors

    // kill the animator in 2 seconds
    _timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(killAnimator:) userInfo:nil repeats:NO];
}

- (void)killAnimator:(NSTimer *)timer {
    [_animator removeAllBehaviors];
    _animator = nil;
    _timer = nil;
}