Warpling
Warpling

Reputation: 2105

Is it possible to add multiple UIGravityBehaviors to a UIDynamicAnimator?

I'm trying to create a view where some items fall along normal gravity vector and others fall along an opposite vector

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

// Normal Gravity
self.gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[ ]];
self.gravityBehavior.gravityDirection = CGVectorMake(0, 1);
[self.animator addBehavior:self.gravityBehavior];

// Inverse Gravity
self.inverseGravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[ ]];
self.inverseGravityBehavior.gravityDirection = CGVectorMake(0, -1);
[self.animator addBehavior:self.inverseGravityBehavior];

I would think then I could just add some items to one behavior and some to the other, but it appears that adding the second gravity behavior overrides the first?

    [self.gravityBehavior        addItem: ballA];
    [self.inverseGravityBehavior addItem: ballB];

Is this true, and if so is there another way to achieve this effect?

Upvotes: 1

Views: 1078

Answers (1)

Andrew Monshizadeh
Andrew Monshizadeh

Reputation: 1784

This behavior is definitely possible. However, it cannot be achieved with one animator using differing UIGravityBehaviors.

One solution would be to use two UIDynamicAnimator instances, each with its own UIGravityBehavior instance. Below is an example of a view controller that has two tap gesture recognizers attached, one for single tap, and one for double. The single will add a view that reacts to normal gravity. The double tap adds a view that reacts to inverted gravity. Obviously the code would need to be tweaked to match your specific requirements.

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UIDynamicAnimator *animator;
@property (nonatomic, strong) UIDynamicAnimator *secondAnimator;

@property (nonatomic, strong) UIGravityBehavior *normalGravity;
@property (nonatomic, strong) UIGravityBehavior *inverseGravity;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];

    self.normalGravity = [[UIGravityBehavior alloc] init];
    [self.animator addBehavior:self.normalGravity];

    self.secondAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    self.inverseGravity = [[UIGravityBehavior alloc] init];
    [self.inverseGravity setAngle:self.normalGravity.angle magnitude:(-1.0 * self.normalGravity.magnitude)];
    [self.secondAnimator addBehavior:self.inverseGravity];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (IBAction)viewWasTapped:(id)sender {
    UIView *v = [[UIView alloc] initWithFrame:CGRectMake(30, 10, 25, 25)];
    v.backgroundColor = [UIColor redColor];
    [self.view addSubview:v];
    [self.normalGravity addItem:v];
}

- (IBAction)viewWasDoubleTapped:(id)sender {
    UIView *v = [[UIView alloc] initWithFrame:CGRectMake(90, 400, 25, 25)];
    v.backgroundColor = [UIColor greenColor];
    [self.view addSubview:v];
    [self.inverseGravity addItem:v];
}

@end

A second option would be to use a UIPushBehavior to simulate inverted gravity. Below is a similarly set up view controller, but without the second animator and using the push behavior.

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UIDynamicAnimator *animator;

@property (nonatomic, strong) UIGravityBehavior *normalGravity;
@property (nonatomic, strong) UIPushBehavior *pushBehavior;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];

    self.normalGravity = [[UIGravityBehavior alloc] init];
    [self.animator addBehavior:self.normalGravity];

    self.pushBehavior = [[UIPushBehavior alloc] initWithItems:@[] mode:UIPushBehaviorModeContinuous];
    [self.pushBehavior setAngle:self.normalGravity.angle magnitude:(-1.0 * self.normalGravity.magnitude)];
    [self.animator addBehavior:self.pushBehavior];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (IBAction)viewWasTapped:(id)sender {
    UIView *v = [[UIView alloc] initWithFrame:CGRectMake(30, 10, 25, 25)];
    v.backgroundColor = [UIColor redColor];
    [self.view addSubview:v];
    [self.normalGravity addItem:v];
}

- (IBAction)viewWasDoubleTapped:(id)sender {
    UIView *v = [[UIView alloc] initWithFrame:CGRectMake(90, 400, 25, 25)];
    v.backgroundColor = [UIColor greenColor];
    [self.view addSubview:v];

    [self.pushBehavior addItem:v];
}

@end

In all situations be sure you are managing which items are being impacted by the UIDynamicAnimator and the various behaviors so that you are not constantly adding to them and bogging the simulation down.

Upvotes: 6

Related Questions