9

我正在编写可扩展托盘视图的代码,该视图使用 UIDynamicAnimator 来实现漂亮的展开/收缩动画。

为了实现逼真的加速效果,我使用UIGravityBehavior我的托盘掉落,直到托盘的“标签”碰到屏幕底部。

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

我尝试UIGravityBehavior从场景中删除,这确实导致动画师最终停止。不过,我无法确定移除重力行为的正确时间,因为一旦一切停止移动,我就需要将其从场景中移除。

我知道重力是一个恒定的力,但我仍然认为一旦一切都具有 0 速度和 0 加速度,它会停止动画师。

最后一个假设是错误的吗?

任何人有类似的问题?

4

3 回答 3

6

你是正确的,一旦一切都停止,动画师应该暂停。

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

  • 为重力和碰撞添加视图
  • 从超级视图和碰撞中删除视图
  • 无法从重力中移除视图

在这种情况下,“鬼物”将永远陨落。

另一个可能的问题(尽管根据您的描述不太可能)是您的项目是否附加到导致无限但小的“反弹”的其他行为。我会检查你的动画师的完整行为列表(记得检查孩子的行为)。特别是我会对任何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];
于 2013-10-15T16:16:42.080 回答
1

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

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

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

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

于 2013-10-15T15:51:10.993 回答
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;
}
于 2015-05-13T17:09:58.930 回答