Accessing presentationLayer of an animation to detect collisions - iphone

I have two objects: one that is moving by animation, and another that is moving when I drag it around with my finger. I want to be able to detect when the two "collide" using CGIntersectsRect. However, I've heard that in order to do this with animations, I need to access the presentation layer to grab values from there. I have no idea how to go about doing this, however. This is the animation code I have:
UIImage *flakeImage = [UIImage imageNamed:#"apple.png"];
UIImageView *flakeView = [[UIImageView alloc] initWithImage:flakeImage];
flakeView.frame = CGRectMake(200, -25.0, 25.0, 25.0);
[self.view addSubview:flakeView];
[UIView beginAnimations:nil context:(flakeView)];
[UIView setAnimationDuration:2];
flakeView.frame = CGRectMake(200, 800.0, 25.0, 25.0); ]
and here is the code for the object I move with my finger:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// get current touch location
UITouch *touch = [[event touchesForView:self.view] anyObject];
CGPoint point = [touch locationInView:self.view];
// update location of the image
basketView.center = CGPointMake(point.x, basketView.center.y);
}
How can I access the presentationLayer of the flakeView animation so that I can detect when the two objects intersect?

You simply need to keep around a reference to both of your views. Then all you have to do is:
if(CGRectIntersectsRect(((CALayer*)basketView.layer.presentationLayer).frame,
((CALayer*)flakeView.layer.presentationLayer).frame)) {
//handle the collision
}

Related

How to connect two buttons( dots) with a line in iOS? [duplicate]

This question already has answers here:
draw line between two points in iphone?
(3 answers)
Closed 9 years ago.
I want to make a project in which I have to touch one dot and connect it with another dot and after connect it to another dot. When I connect one dot with another dot the line will create between them.
Actually when I click/ touch one dot The line will show and When I touch second dot the line will create between the two dots.
I am not able to do this yet, I am trying and searching on the net but unable to find the solution yet.
This is my need Like this one https://play.google.com/store/apps/details?id=zok.android.dots&hl=en
I think this is done by UIGesture Recogniser? Or is this something else? How I can achieve this?
Any Idea or suggestions from experts would be highly welcome.
Modify this code according to your requiements
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *currentColor = [UIColor blackColor];
CGContextSetStrokeColorWithColor(context, currentColor.CGColor);
CGContextSetLineWidth(context, 2.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, touchStart.x, touchStart.y);
CGContextAddLineToPoint(context, touchEnd.x, touchEnd.y);
CGContextStrokePath(context);
#Nisha:
Make gloabal instances of CGPoint touchStart and touchEnd and get them like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
touchEnd = CGPointZero;
touchStart = CGPointZero;
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
NSLog(#"start point >> %#",NSStringFromCGPoint(point));
touchStart = point;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
touchEnd = point;
[self setNeedsDisplay];
}
You can store the touched locations in two different CGPoint with the help of the touchedEnded method.
Then, when you have your two points, you can add a new UIView as subview which is aware of the two CGPoint and will draw a line in its drawRect method. Or do it in the current view, by calling [view setNeedsDisplay] to trigger its own drawRect method.
Check out this link.
If it is possible for you please get the coordinates of two button with UI touch methods. You can find the touched locations in two different CGPoint with the help of the touchedEnded method.To find touch location documentation is here
After getting the location of UIButtons on your view you can draw the line between then with this method-
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor]CGColor]);
CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
Hope this helps
Try below steps.
Create one subclass of UIView. Add your UIButtons on it.
Implement Touches delgates, like touchesBegan, moved, end.
Inside touchesBegan check if touch isinsideview:myButton1 then make a flag true.
EDIT:
UITouch *touch = [[UITouch alloc] init];
touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
if(CGRectContainsPoint(myButton1.frame, point))
NSLog(#"Inside myButton1");
Another way to test if subview is hit by touches is
CGPoint pt = [[touches anyObject] locationInView:self.view];
UIView *touchedView = [self.view hitTest:pt withEvent:event];
inside touches moved check if flag true then drawline()... and keep checking if touches are in insideview:myButton2. call setNeedsDisplay.
Now you will get number of ways and sample code to draw line in UIView. Just apply above logic.

How to call touchesBegan on a layer that is a sublayer of a UIWebView

I have a layer that I have added as a sublayer of a UIWebView. I would like to be able to touch and drag the layer around. Before I added the UIWebView, I was able to touch and drag around the layer without a problem.
Here's where I create the layer and add it as a sublayer
- (void)viewDidLoad
{
[super viewDidLoad];
starLayer = [CALayer layer];
starLayer.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage
imageNamed:#"star"]].CGColor;
starLayer.shadowOffset = CGSizeMake(0, 3);
starLayer.shadowRadius = 5.0;
starLayer.shadowColor = [UIColor blackColor].CGColor;
starLayer.shadowOpacity = 0.8;
starLayer.frame = CGRectMake(235.0, 200.0, 35.0, 35.0);
[self.aboutWebView.layer addSublayer:starLayer];
[self moveStar];
}
Here's my moveStar method
- (void)moveStar
{
CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:#"position"];
NSMutableArray *values = [NSMutableArray array];
[values addObject:[NSValue valueWithCGPoint:CGPointMake(250.0, 50.0)]];
[values addObject:[NSValue valueWithCGPoint:CGPointMake(250.0, 250.0)]];
[move setValues:values];
[move setDuration:1.0];
[move setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
// Add the animation to the layer to be animated
[starLayer addAnimation:move forKey:#"moveAnimation"];
}
So far so good. The star falls onto the screen and lands a bit above the middle. But when I try and touch the star nothing happens. touchesBegan never gets called for some reason.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.aboutWebView];
[starLayer setPosition:point];
}
Is the reason I can't touch the star when it's a sublayer of aboutWebView because a touch is reported to a view, not a layer?
Does anyone have any suggestions on how to make the star as a sublayer of aboutWebView touchable? Is it possible?
Thank you!
In your layer... try to set the UIGestureRecognizer property cancelsTouchesInView to NO.
"Setting this value to NO instructs the recognizer to deliver all touches to the underlying view, even when it has recognized the sequence"
something like that:
UIGestureRecognizer *touches = [UIGestureRecognizer alloc];
//or
//UIGestureRecognizer *touches = [[UIGestureRecognizer alloc] initWithTarget:self action:#selector(touchesAction:)];
touches.enabled=YES;
[yourLayer addGestureRecognizer:touches];
touches.cancelsTouchesInView=NO;

iOS CGRectIntersectsRect with multiple instances of UIImageView?

I have a layout for an application that needs to detect when an image collides with another image.
Here, the user creates multiple 'balls', the same instance of a UIImageView named 'imgView', on the screen by tapping the location they desire:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *myTouch = [[event allTouches] anyObject];
imgView = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
imgView.image = [UIImage imageNamed:#"ball.png"];
[self.view addSubview:imgView];
imgView.center = [myTouch locationInView:self.view];
}
(imgView is declared as a UIImageView in the header) :
UIImageView *imgView;
Now, I also have an image called 'staff'. It is a long bar that pans horizontally across the screen. I want the image 'staff' to detect EVERY collision it has with the variable 'imgView', or the balls the user has placed on the screen.
Therefore, the user could tap 10 different places on the screen and and 'staff' should be able to catch every single one.
I use this CGRectIntersectsRect code thats activated by an NSTimer:
-(void)checkCollision {
if( CGRectIntersectsRect(staff.frame,imgView.frame)) {
[self playSound];
}
}
However, the intersection is only detected with the LAST instance, or 'ball' the user created. The staff reacts to that one, but pans over the rest. Any help to fix my code to detect all instances would be greatly appreciated.
Each time you create a new ball, you are overriding your imgView instance variable. Thus, your checkCollision method only sees the latest value of imgView, namely the last ball created.
Instead, you could keep track of every ball on screen in an NSArray, then check for collisions against every element in that array. To do that, replace your imgView instance variable with, say:
NSMutableArray *imgViews
Then, sometime early on, say in viewDidLoad initialize the array:
imgViews = [[NSMutableArray alloc] init]
In -touchesEnded:withEvent: add the new UIImageView to the array:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *myTouch = [[event allTouches] anyObject];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(40, 40, 40, 40)];
imgView.image = [UIImage imageNamed:#"ball.png"];
[self.view addSubview:imgView];
imgView.center = [myTouch locationInView:self.view];
[imgViews addObject:imgView]
}
And finally, in checkCollision iterate through your array and perform your check on every element
- (void)checkCollision {
for (UIImageView *imgView in imgViews) {
if( CGRectIntersectsRect(staff.frame,imgView.frame)) {
[self playSound];
}
}

Changing an Image on TouchesBegan

Hey, I am trying to change the image when the user touches the ImageView. At the moment the ImageView has the image "heart1.png" so when i touch the ImageViewer on screen it will change to "heart2.png" this is my code, help is much appreciated:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:self.view];
if (location.x >= imageView.frame.origin.x && location.x <= imageView.frame.origin.x + imageView.frame.size.height && location.y >= imageView.frame.origin.y && location.y <= imageView.frame.origin.y + imageView.frame.size.height) {
imageView.image = [UIImage imageNamed:#"heart2.png"];
}
}
That looks like a problem to me:
if (location.x >= imageView.frame.origin.x && location.x <= imageView.frame.origin.x + imageView.frame.size.--> height <--
should be width there probably :)
Rather than fool around with checking coordinates against a bounding rect, why don't you simplify things by using interface building to place a custom (i.e. invisible) UIButton right over the image? Then you can hook up touchesUpInside to an IBAction method. Then there's no awkward if statements in your code and less chance of making mistakes like this.
Consider using an UIButton instead.
[button setImage:[UIImage imageNamed:#"heart1.png"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"heart2.png"] forState:UIControlStateHighlighted];
There is no need to find location of view, You don't have to do move UIImageView. Simply do this:
-(void)viewDidLoad
{
// imageView is your UIImageView
imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0.0, 0.0, 320.0, 480.0)];
[imageView setImage:[UIImage imageNamed:#"heart1.png"]];
[imageView setUserInteractionEnabled:YES];
// Make sure userInteractionEnable is set to be YES; otherwise the touch event will be happen on UIView not on ImageView;
[self.view addSubview:imageView];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches]anyObject];
if([touch view]==imageView)
[imageView setImage:[UIImage imageNamed:#"heart2.png"]];
}

iPhone/Cocoa Animation having effect I don't understand

I have created a basic unlock slider like you use to unlock the iPhone.
In my touchesEnded function, I use an animation to move the slider back to home.
The first time the user moves the slider (and doesn't actually hit the end to unlock), the animation works fine and the slider moves back to home.
However, the next time the user tries to move the slider (and touchesMoved gets called), the slider gets pushed offscreen (to the left) by the amount they had slid the slider before.
So, what's happening?
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.dragging = NO;
self.startX = 0;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
self.unlock.frame = CGRectMake(0, 0, 75, 35);
[UIView commitAnimations];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (!self.dragging ) { return; }
UITouch *touch = [[event allTouches] anyObject];
CGPoint point = [touch locationInView:self];
int offset = point.x - self.startX;
NSLog(#"%i", offset);
if (offset > (LENGTH-SIZE)) {
offset = (LENGTH-SIZE);
}
CGAffineTransform myTransform = CGAffineTransformMakeTranslation(offset, 0.0);
[self.unlock setTransform:myTransform];
if (point.x >= (LENGTH-(SIZE-self.startX))) {
NSLog(#"Slider reached end");
[self.sliderView removeFromSuperview];
}
}
EDIT
Here's the working function, cleaned up a bit, though it could be better (thanks to a couple answered questions on SO from one hacker). I'm going to blog the complete module since there doesn't seem to be a standard widget:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (!self.dragging ) { return; }
UITouch *touch = [[event allTouches] anyObject];
CGPoint point = [touch locationInView:self];
if (point.x >= (LENGTH-(SIZE-self.startX))) {
NSLog(#"Slider reached end");
[self.sliderView removeFromSuperview];
}
// offset based on where the user clicked the slider
int offset = point.x - self.startX;
if (offset > (LENGTH-SIZE)) {
offset = (LENGTH-SIZE);
}
//move the slider
CGRect oldFrame = self.unlock.frame;
CGRect newFrame = CGRectMake(self.frame.origin.x+offset, self.frame.origin.y-5, oldFrame.size.width, oldFrame.size.height);
self.unlock.frame = newFrame;
}
IN your previous question I didn't realize you were moving the slider by changing its transform. Mixing up motion by changing its frame and transform is going to be moderately complicated.
Maybe you could try doing something more like this:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (!self.dragging ) { return; }
UITouch *touch = [[event allTouches] anyObject];
CGPoint point = [touch locationInView:self];
int offset = point.x - self.startX;
NSLog(#"%i", offset);
if (offset > (LENGTH-SIZE)) {
offset = (LENGTH-SIZE);
}
CGRect oldFrame = self.frame;
CGRect newFrame = CGRectMake(oldFrame.origin.x+offset, oldFrame.origin.y, oldFrame.size.width, oldFrame.size.height);
self.frame = newFrame;
if (point.x >= (LENGTH-(SIZE-self.startX))) {
NSLog(#"Slider reached end");
[self.sliderView removeFromSuperview];
}
}
That moves the slider by modifying its frame, not its translation, which should cause it to interact better with the snap back animation. Alternatively, rather than resetting the frame in the implicit animator you could reset the translation there.
You need to reset the view transform in your touchesEnded handler:
[self.unlock setTransform:CGAffineTransformIdentity];
Try that instead of animating the frame property.