I'd like to make it when the user shakes the device the ball rattles around inside the object on screen. I'm assuming I need to set up an invisible box for it to collide with. It doesn't matter if it moved randomly or follows a predefined path, whichever is easiest.
I think I understand the "Activate on shake" part of the code, just the ball/object movement I'm not sure of
This should work:
//You need an #property (nonatomic, strong) UIDynamicAnimator *animator; in your .h
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.viewToBounceAroundIn];
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:#[self.viewThatBouncesAround]];
collision.translatesReferenceBoundsIntoBoundary = YES;
[self.animator addBehavior:collision];
UIPushBehavior *push = [[UIPushBehavior alloc] initWithItems:#[self.viewThatBouncesAround] mode:UIPushBehaviorModeInstantaneous];
push.magnitude = 1; //Play with this, it's how much force is applied to your object
push.angle = 0; //play with this too
[self.animator addBehavior:push];
I typed this away from a compiler - let me know if it works. The idea is that you use UIKitDynamics as a physics engine, use a UICollisionBehavior to let the item bounce around inside the box, and a UIPushBehavior to apply the initial force.
If the item slows down too quickly for you, or loses too much energy when it bounces off walls, you can adjust its properties:
UIDynamicItemBehavior *behavior = [[UIDynamicItemBehavior alloc] initWithItems:#[self.itemThatBouncesAround]];
behavior.friction = 0; //no friction. play with this.
behavior.elasticity = 1;; //completely elastic, play with this.
[self.animator addBehavior:behavior];
Related
I have two UIViews.i want to set different gravitydirection vector for these two views for making **attraction**.and i am doing this by having two gravitybehavior but when i am adding these two to animator.
it's not working as **attracting** to each other.
also giving warning like :-Multiple gravity behavior per animator is undefined and may assert in the future.
Below is the code i am using to make Attractiuon effect b/w two views.
gravity behavior is to make the gravity animation.and animator is dynamic animator.
UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:#[v1]];
CGVector vector = CGVectorMake(1.0, 1.0);
[gravityBehavior setGravityDirection:vector];
UIGravityBehavior *gravityBehavior1 = [[UIGravityBehavior alloc] initWithItems:#[v2]];
CGVector vector1 = CGVectorMake(-1.0, -1.0);
[gravityBehavior1 setGravityDirection:vector1];
gravityBehavior.magnitude=1000;
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
[self.animator addBehavior:gravityBehavior];
[self.animator addBehavior:gravityBehavior1];
Please Help!
Thanks for the Help!!
With UIInterpolatingMotionEffect, twist the iPhone, and you can have an image move.
Now: imagine a red block you will "bounce around" on screen, using UICollisionBehavior and UIDynamicItemBehavior. When the user twists the iPhone: I want the boxes to "start moving" WITH SIMILAR PHYSICS FEEL to using UIInterpolatingMotionEffect.
http://tinypic.com/player.php?v=b67mlc%3E&s=8#.VBVEq0uZNFx
Aside:UX explanation: the bouncy effect (example: SMS on iPhone) has the same "feel" as the parallax image effect in iOS. (By "feel" I really just mean the same speed, acceleration.) This would be a third effect: like parallax it would "move things slightly" but they would "keep moving", bouncing a little. (You could say, somewhat combining the feel of bouncy-lists-effect and and parallax-images-effect.)
Now: it's relatively easy to do what I describe, using CMAccelerometerData, and applying pushes using UIPushBehaviorModeInstantaneous. But it's a lot of messy code.
In contrast, UIInterpolatingMotionEffect is ridiculously easy to use.
Essentially, how can I get the values from UIInterpolatingMotionEffect (which I will then use as pushes). Cheers!
Similar thought ...
Simply display the values of UIInterpolatingMotionEffect?
It asks simply: how can one easily "just get" the values from UIInterpolatingMotionEffect ? ie, it seems incredible one has to go to the effort of carefully subclassing CALayer, etc.
It's an interesting notion of updating some behavior via the UIInterpolatingMotionEffect, though I don't suspect it's designed for that. If you want to update behaviors based upon accelerometer information, I personally would have thought that the CMMotionManager is ideal for that purpose.
The desired UX isn't entirely clear from the video clip, but it looks like that video is having things continue to slide in the direction the phone is tilted until you stop tilting the phone. If that's what you want, I'd be inclined to marry CMMotionManager with a UIKit Dynamics UIGravityBehavior:
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:container];
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:container.subviews];
collision.translatesReferenceBoundsIntoBoundary = YES;
[self.animator addBehavior:collision];
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:container.subviews];
gravity.gravityDirection = CGVectorMake(0, 0);
[self.animator addBehavior:gravity];
self.motionManager = [[CMMotionManager alloc] init];
typeof(self) __weak weakSelf = self;
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
if (weakSelf.referenceAttitude == nil) {
weakSelf.referenceAttitude = motion.attitude;
} else {
CMAttitude *attitude = motion.attitude;
[attitude multiplyByInverseOfAttitude:weakSelf.referenceAttitude];
gravity.gravityDirection = CGVectorMake(attitude.roll * 5.0, attitude.pitch * 5.0);
}
}];
If you want them to move precisely in accordance to the attitude, stopping the movement when you stop moving the device (like UIInterpolatingMotionEffect does), you could use a UIAttachmentBehavior, something like:
UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:viewToAttachTo attachedToAnchor:viewToAttachTo.center];
[self.animator addBehavior:attachment];
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 1.0 / 20.0;
typeof(self) __weak weakSelf = self;
CGPoint originalAnchorPoint = viewToAttachTo.center;
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
if (weakSelf.referenceAttitude == nil) {
weakSelf.referenceAttitude = motion.attitude;
} else {
CMAttitude *attitude = motion.attitude;
[attitude multiplyByInverseOfAttitude:weakSelf.referenceAttitude];
attachment.anchorPoint = CGPointMake(originalAnchorPoint.x + attitude.roll * 10.0, originalAnchorPoint.y + attitude.pitch * 10.0);
}
}];
Note, in both of those examples, I'm applying the adjustments to the behaviors as the device shifts from the orientation of the device when the user started the app. Thus I capture a CMAttitude property, referenceAttitude to be the attitude of the device when the user fired up the app, and then uses multiplyByInverseOfAttitude on subsequent updates to apply gravity in relation to that original orientation. You could, obviously, use a predetermined attitude if you wanted, too.
But hopefully the above illustrates one approach to tackling this sort of UX.
I am trying to implement UIDynamicAnimator effects on a scrollView with many elements. I couldn't find too much information about it anywhere ,so i just need some starting point.
So i have my scrollView, with many UIViews in it. i want to make any kind of animation to its "subViews" while scrolling.
Tried that-nothing happens .
//DYNAMICS ANIMATIONS
UIDynamicAnimator *dynamic=[[UIDynamicAnimator alloc] initWithReferenceView:scroller];
UIGravityBehavior *gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:mainCellsArray];
[dynamic addBehavior:gravityBeahvior];
When mainCellsArray holds all the "subViews" of the scroller .
EDIT:
I have a strong property for the dynamic .
The array holds my costume classes pointers , that each class is a UIView subclass, and they all the scroller children's.
First, make sure you define your animator as a property, not solely as a local variable (and I tend to use animator for the name of that, to avoid confusion with the #dynamic keyword):
#property (strong, nonatomic) UIDynamicAnimator *animator;
Then instantiate the animator:
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.scrollView];
And add the gravity:
UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:#[viewToAnimate]];
[self.animator addBehavior:gravityBehavior];
If you want them to stop when they hit the bottom of the contentSize of the scroll view, you can't use the typical translatesReferenceBoundsIntoBoundary setting. You have to make a path yourself, e.g. something like:
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:#[viewToAnimate]];
CGRect contentSizeRect = {CGPointZero, self.scrollView.contentSize};
UIBezierPath *path = [UIBezierPath bezierPathWithRect:contentSizeRect];
[collision addBoundaryWithIdentifier:#"contentSize" forPath:path];
[self.animator addBehavior:collision];
Or, if you want them to fly off the scroll view, you probably want to remove them when they no longer intersect the contentSize of the scroll view:
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:#[viewToAnimate]];
UIGravityBehavior __weak *weakGravity = gravity;
CGRect contentSizeRect = {CGPointZero, self.scrollView.contentSize};
gravity.action = ^{
if (!CGRectIntersectsRect(contentSizeRect, viewToAnimate.frame)) {
NSLog(#"removing view");
[viewToAnimate removeFromSuperview];
[self.animator removeBehavior:weakGravity];
}
};
[self.animator addBehavior:gravity];
I'm following along with the Stanford ios7 course, chapter 8, where the instructor builds a simplified Tetris game, with colored blocks dropping from above, where you have to fill in rows. After adding gravity behavior to make the blocks fall, he adds a collision behavior (note the property below), lazily instantiates it and, while doing that, he sets the bounds like this
_collider.translatesReferenceBoundsIntoBoundary = YES;
which makes the blocks collide with the bottom of the screen (rather than falling through) so they can stack on top of each other. He then adds the collision behavior to the animator property, and, as a final step, in the drop method, he adds the dropView (which are the blocks) to the collision behavior. When he runs, the blocks hit the bottom and stack ontop of each other. When I run, using the code below, the blocks continue to fall through the bottom of the screen (on the simulator). In other words, there is no stacking.
Can you see why the collision behavior might not be working.
ViewController
#property (strong,nonatomic) UIDynamicAnimator *animator;
#property (strong, nonatomic) UIGravityBehavior *gravity;
#property (strong, nonatomic) UICollisionBehavior *collider;
#end
#implementation DropItViewController
static const CGSize DROP_SIZE = { 40, 40 };
- (IBAction)tap:(UITapGestureRecognizer *)sender {
[self drop];
}
- (UICollisionBehavior *)collider
{
if (!_collider){
_collider = [[UICollisionBehavior alloc] init];
_collider.translatesReferenceBoundsIntoBoundary = YES;
[self.animator addBehavior:_collider];
}
return _collider;
}
- (UIDynamicAnimator *)animator
{
if (!_animator) {
_animator = [[UIDynamicAnimator alloc] init];
}
return _animator;
}
-(UIGravityBehavior *)gravity
{
if (!_gravity) {
_gravity = [[UIGravityBehavior alloc] init];
[self.animator addBehavior:_gravity];
}
return _gravity;
}
Add the dropView to the collider in the drop method
-(void)drop
{
CGRect frame;
frame.origin = CGPointZero;
frame.size = DROP_SIZE;
int x = (arc4random() % (int)self.gameView.bounds.size.width) / DROP_SIZE.width;
frame.origin.x = x * DROP_SIZE.width;
UIView *dropView = [[UIView alloc] initWithFrame:frame];
dropView.backgroundColor = self.randomColor;
[self.gameView addSubview:dropView];
[self.gravity addItem:dropView];
[self.collider addItem:dropView];
}
When you instantiate your UIDynamicAnimator, use initWithReferenceView instead of init. Then when you use translatesReferenceBoundsIntoBoundary, it will know what reference bounds to use.
I'm in the same course and thought I was seeing the same issue. However, try running in the 4.0 inch simulator. Your squares are probably collecting just offscreen (outside the bounds of a 3.5 inch screen).
I want this item to Pop up briefly before being pulled down by gravity. Right now if I add the push effect it launches down.
//Animate this new fake button
UIGravityBehavior* gravity = [[UIGravityBehavior alloc] initWithItems:#[fakeButton]];
[gravity setMagnitude:1.0f];
UIPushBehavior *upFlick = [[UIPushBehavior alloc]initWithItems:#[fakeButton] mode:UIPushBehaviorModeInstantaneous];
[upFlick setAngle:M_PI/3 magnitude:1.0f];
[self.animator addBehavior:gravity];
[self.animator addBehavior:upFlick];
You should make either the angle or the magnitude negative, and that should make it go up (and to the side). If you want it to go straight up, you should also change the angle to M_PI/2.