Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to identify the collision of two imageviews when they are moving with animation?

I working on the getting the alert when two views collide, here I'm using two image views moving in different directions they will collide in a point. I used basic Animation code to move these images in their respective directions.( note :- not only like in following image, however if they collide in aspect we need to show alert )

enter image description hereenter image description here

like image 332
LOVE_2_CODE Avatar asked Mar 03 '14 04:03

LOVE_2_CODE


2 Answers

If using standard view animation (e.g. the block-based animateWithDuration), you can set up a CADisplayLink (kind of like a timer, but called every time the display is updated) and then compare the the frame values for the two views and see if they intersect. The trick is, though, that during an animation, the frame of a view is the "final destination" frame, so if you want the the frame as it is while an animation is in progress, you can look at its "presentation layer".

- (void)startDisplayLink
{
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)stopDisplayLink
{
    [self.displayLink invalidate];
    self.displayLink = nil;
}

- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
    CALayer *view1PresentationLayer = self.view1.layer.presentationLayer;
    CALayer *view2PresentationLayer = self.view2.layer.presentationLayer;

    if (CGRectIntersectsRect(view1PresentationLayer.frame, view2PresentationLayer.frame)) {

        NSLog(@"collision");                              // report the collision; show alert if you want

        [self.view1.layer removeAllAnimations];           // stop the animation if you want
        [self.view2.layer removeAllAnimations];

        self.view1.frame = view1PresentationLayer.frame;  // but make sure to reset the frame to be the current position
        self.view2.frame = view2PresentationLayer.frame;

        [self stopDisplayLink];                           // stop the display link if we don't need it any more
    }
}

In the above method, I'm assuming you want to stop the animation (which involves not only removing the animations, but resetting the final destination to reflect where the views currently are), but do whatever you want.

Thus, you can then start the display link, initiate the animation, and the display link selector method will tell us if there's a collision:

[self startDisplayLink];

[UIView animateWithDuration:1.0 animations:^{
    self.view1.frame = CGRectMake(...);
    self.view2.frame = CGRectMake(...);
}];

Note, in iOS 7, you might want to check out UIKit Dynamics, in which you can add behaviors to drive the movement of views, and you can also define a UICollisionBehavior to identify when such a collision takes place. Using UIKit Dynamics is probably beyond the scope of this question, but you should refer to Apple's WWDC 2013 videos Getting Started with UIKit Dynamics and Advanced Techniques with UIKit Dynamics.

But, for example, in iOS 7 you can abandon the animateWithDuration, and instead animate with UIKit Dynamics, in which you could tell two views to snap to particular locations on the screen but detect when they collide:

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

UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.view1, self.view2]];
itemBehavior.allowsRotation = NO;
itemBehavior.resistance = 10.0;  // you can change this to affect the speed of the attachment behaviors
[self.animator addBehavior:itemBehavior];

UISnapBehavior *snap1 = [[UISnapBehavior alloc] initWithItem:self.view1 snapToPoint:newPoint1];
[self.animator addBehavior:snap1];

UISnapBehavior *snap2 = [[UISnapBehavior alloc] initWithItem:self.view2 snapToPoint:newPoint2];
[self.animator addBehavior:snap2];

// You can, alternatively use the following attachment behaviors, instead of the above
// snap behaviors, and you can then use resistance to control the speed with which they move
//
// UIAttachmentBehavior *attachment1 = [[UIAttachmentBehavior alloc] initWithItem:self.view1 attachedToAnchor:self.view1.center];
// [self.animator addBehavior:attachment1];
// attachment1.frequency = 1.0;
// attachment1.anchorPoint = newPoint1;
//
// UIAttachmentBehavior *attachment2 = [[UIAttachmentBehavior alloc] initWithItem:self.view2 attachedToAnchor:self.view2.center];
// [self.animator addBehavior:attachment2];
// attachment2.frequency = 1.0;
// attachment2.anchorPoint = newPoint2;

UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.view1, self.view2]];
collision.collisionDelegate = self;
[self.animator addBehavior:collision];

And have a UICollisionBehaviorDelegate method that does something when there is a collision, e.g. remove the behaviors, effectively ending the animation:

- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item1 withItem:(id<UIDynamicItem>)item2 atPoint:(CGPoint)p
{
    [self.animator removeAllBehaviors];  // for example, if they collide, you could remove the behaviors so they stop

    // show alert if you want to
}
like image 143
Rob Avatar answered Sep 18 '22 14:09

Rob


If you are targeting iOS 7 and above then you could look into UIKit Dynamics

https://developer.apple.com/library/ios/samplecode/DynamicsCatalog/Introduction/Intro.html

like image 32
Garfield81 Avatar answered Sep 19 '22 14:09

Garfield81