UIImageView animating on UIBezierPath not detecting collision - objective-c

I have a UIImageView travelling on a UIBezierPath:
- (void)viewDidLoad {
[super viewDidLoad];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(60, 60)];
[path addLineToPoint:CGPointMake(70, 70)];
[path addLineToPoint:CGPointMake(80, 120)];
[self.thingy setImage:[UIImage imageNamed:#"titles.png"]];
[self.thingy.layer setPosition:CGPointMake(60, 60)];
[self.view.layer addSublayer:self.thingy.layer];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = path.CGPath;
anim.rotationMode = kCAAnimationRotateAuto;
anim.duration = 8.0;
[self.thingy.layer addAnimation:anim forKey:#"move"];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(tester)
userInfo:nil
repeats:YES];
}
Goal: I want to detect whether thingy collides with a barrier. Here is tester:
- (void) tester {
NSLog(#"calling method");
if (CGRectIntersectsRect(self.thingy.frame, self.barrier.frame)) {
NSLog(#"collided");
}
}
barrier and thingy are both UIImageViews declared as weak and nonatomic in the header file.
Problem: tester is always being called every second, but when the two UIImageViews collide nothing happens. Any help to try and make this work?

Since thingy is being animated, you won't get accurate values from its frame. If I recall correctly, once the animation starts, the view will immediately have the final frame from the end of the animation. If you want to test thingy's frame while it is in motion, you have to test the frame of thingy's layer's presentationLayer. Something like this:
- (void) tester {
NSLog(#"calling method");
if (CGRectIntersectsRect(((CALayer *)[self.thingy.layer presentationLayer]).frame, self.barrier.frame)) {
NSLog(#"collided");
}
}
Note: If your barrier is in motion too, you'll have to use its presentationLayer as well.

Related

How can I hide my UIImageView after the animation is completed?

How can I hide my UIImageView after the animation is completed? My animation is working fine when I segue into the view. I want to be a able to hide the image after the animation is complete. How might I do that?
-(void) animate
{
NSLog(#"Animate");
CGPoint startPoint = [pickerCircle center];
CGPoint endPoint = [pickerButton1 center];
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath, NULL, startPoint.x, startPoint.y);
CGPathAddLineToPoint(thePath, NULL, endPoint.x, endPoint.y);
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.duration = 3.f;
animation.path = thePath;
animation.repeatCount = 2;
animation.removedOnCompletion = YES;
[pickerCircle.layer addAnimation:animation forKey:#"position"];
pickerCircle.layer.position = endPoint;
}
Set the animation's delegate property to self like so:
animation.delegate = self;
And then you can use:
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
// hide your UIImage here
}
It will be called when the animation is done.
Did you try completion? Here example 4.2there is an example about showing and hiding an view with using completion.
I don't know animations well but I think completion should work.

SpriteKit: textureFromNode method not working in initWithSize method

This doesn't work – the newNode's texture is nil:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKSpriteNode *node = [SKSpriteNode spriteNodeWithColor:[UIColor orangeColor] size:CGSizeMake(100, 100)];
SKTexture *texture = [self.view textureFromNode:node];
SKSpriteNode *newNode = [SKSpriteNode spriteNodeWithTexture:texture];
[self addChild:newNode];
}
return self;
}
This does work:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKSpriteNode *node = [SKSpriteNode spriteNodeWithColor:[UIColor orangeColor] size:CGSizeMake(100, 100)];
SKAction *createNewNode = [SKAction runBlock:^{
SKTexture *texture = [self.view textureFromNode:node];
SKSpriteNode *newNode = [SKSpriteNode spriteNodeWithTexture:texture];
[self addChild:newNode];
}];
[self runAction: createNewNode];
}
return self;
}
Can someone please explain why!? Thanks!
In case you're wondering why the node is not added the scene: The Apple documentation for textureFromNode states: "The node being rendered does not need to appear in the view’s presented scene."
My guess is: at the point where you are creating the sprite node (inside the scene's initWithSize:) the node graph is not fully set up yet. The scene isn't connected with (presented by) the view, and therefore it is not set up for rendering.
It's quite possible that SKSpriteNode textures for sprites using a color are only created after the scene is presented. This would explain why running the block action works.
You can probably fix this also by simply moving the code from initWithSize: to the scene's didMoveToView method, which is called after the scene was presented on the view.

Detecting position tapped with UITapGestureRecognizer

Hi so I was wondering if there was any way possible to get the position that was touched using UITapGestureRecognizer to recognize taps on the background.
-(void)handleTapGesture:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateRecognized)
{
if ( bomb == nil)
{
bomb = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bomb.png"]];
bomb.center = CGPointMake(10, 1);
bomb.frame = CGRectMake(113,123,67,67);
[self.view addSubview:bomb];
//[self performSelector:#selector(Update) withObject:nil afterDelay:1];
NSLog(#"Spawned bomb");
timer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(dropBomb) userInfo:nil repeats:YES];
}
}
}
Sure there is! Use locationInView: to get the CGPoint location of the touch on your image view.
CGPoint touchLocation = [sender locationInView:sender.view];
Or, if you are allowing multiple touches, you can use the following to specify which touch you are interested in.
CGPoint otherTouchLocation = [sender locationOfTouch:0 inView:sender.view];

How to elegantly transition images in slide show?

After some research on here I found a solution to creating a slide show of images in an iphone app. All working fine, currently the images show one after another.
My question is, can I make the images cross dissolve/fade rather than just appearing, and if so could I get some advice on that.
My Code
.m
}
int topIndex = 0, prevTopIndex = 1;
- (void)viewDidLoad
{
imagebottom = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,160,240)];
[self.view addSubview:imagebottom];
imagetop = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,160,240)];
[self.view addSubview:imagetop];
imageArray = [NSArray arrayWithObjects:
[UIImage imageNamed:#"image1.png"],
[UIImage imageNamed:#"image2.png"],
[UIImage imageNamed:#"image3.png"],
[UIImage imageNamed:#"ip2.png"],
nil];
NSTimer *timer = [NSTimer timerWithTimeInterval:5.0
target:self
selector:#selector(onTimer)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[timer fire];
[super viewDidLoad];
}
-(void)onTimer{
if(topIndex %2 == 0){
[UIView animateWithDuration:5.0 animations:^
{
imagebottom.alpha = 0.0;
}];
imagetop.image = [imageArray objectAtIndex:prevTopIndex];
imagetop.image = [imageArray objectAtIndex:topIndex];
}else{
[UIView animateWithDuration:5.0 animations:^
{
imagetop.alpha = 1.0;
}];
imagetop.image = [imageArray objectAtIndex:topIndex];
imagebottom.image = [imageArray objectAtIndex:prevTopIndex];
}
prevTopIndex = topIndex;
if(topIndex == [imageArray count]-1){
topIndex = 0;
}else{
topIndex++;
}
You have a bunch of options. If you have a "container" view, you can make a new UIImageView transparent (alpha = 0), then use a UIView animation block to fade one image in and the other out (or leave alpha = 1 on both and slide one in from any side you want.
For instance, you have your main view, self.view. You have one UIImageView *oldView, that is now at rect (0,0,320,100) and you want to slide it to the right as you slide a newView imageView in. First you set the newView frame to (-320,0,320,100) then [self.view addSubview newView]. To animate the change:
[UIView animateWithDuration:2 animations:^
{
oldView.frame = CGRectMake(320, 0, 320, 100);
newView.frame = CGRectMake(0,0,320, 100);
}
completion:^(BOOL finished)
{
[oldView removeFromSuperView];
} ];
You also have the option of using UiView's
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion
which gives you more/different options (and its less work too!). For instance, using the same basic objects in the first example, but the newView having the same frame as the oldView:
transitionFromView:oldView toView:newView duration:2 options: UIViewAnimationOptionTransitionCrossDissolve completion:^(BOOL finished)) { /*whatever*/}];

addSubview in Cocoa framework

I have a regular DocumentView class in a Window. I have the following code once a user presses a button:
- (void)handleButtonPress:(NSNotification *)note{
// draw new graph view
EDGraphView *graph = [[EDGraphView alloc] init];
[self addSubview:graph];
[self setNeedsDisplay:TRUE];
NSLog(#"Button was pressed");
}
This gets called correctly because I get the output "Button was pressed" every time I click on the button. In addition to that the drawRect method below of the view gets called as well.
- (void)drawRect:(NSRect)dirtyRect
{
NSRect bounds = [self bounds];
[[NSColor whiteColor] set];
[NSBezierPath fillRect:bounds];
for(EDGraphView *graph in [self subviews]){
[graph setNeedsDisplay:TRUE];
NSLog("calling set needs display on graph object!");
}
}
However when I go in the EDGraphView class and edit the drawRect method to look like the following
- (void)drawRect:(NSRect)dirtyRect
{
NSLog(#"redrawing graph view.");
}
It never gets called! I must be missing something about the whole setNeedsDisplay and drawRect process.
Any suggestions?
hi Normaly you don't call setneeddisplay into a drawrect.
have you tried (with the super)? :
- (void)drawRect:(NSRect)dirtyRect
{
NSRect bounds = [self bounds];
[[NSColor whiteColor] set];
[NSBezierPath fillRect:bounds];
[super drawRect:rect];
}
Got it...I needed to make the following init call on my subview:
EDGraphView *graph = [[EDGraphView alloc] initWithFrame:bounds];
Now it calls the drawRect method!