How to make button enabled during animation - iphone

I'm using CAKeyframeAnimation.
-(IBAction)start:(id)sender {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(600, 150)];
[path addLineToPoint:CGPointMake(600, 300)];
[path addLineToPoint:CGPointMake(450, 300)];
[path addLineToPoint:CGPointMake(450, 150)];
[path addLineToPoint:CGPointMake(600, 150)];
CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:#"position"];
move.path = path.CGPath;
move.duration = 6.0f;
move.repeatCount = 100;
[testButton.layer addAnimation:move forKey:#"move"];
I need to make possible to use button while it moving.
Also I tried use touches detection but its only working with button while it stop.
Is it possible?
Thanks.

Really! you want the user to press the button while it is animating? People prefer to turn off interaction during animation. But anyway it this is your interaction then so be it. Have you tried - [yourButton setUserInteractionEnabled:TRUE];
Normally during any UIView animations we need to simply put UIViewAnimationOptionAllowUserInteraction as the animation option and thats it. Since you have gone more deeper in using CoreAnimation and layers, not sure what options make it work there. Please let us know how it works out for you...
UPDATE: This problem looked interesting to me. So I was just trying this out in Xcode. Here is what I found. To hit a moving button, you need to do hit testing on the button's .layer.presentationLayer property (need QuartzCore for this) in your view controller.
Internally, The animation is just eye candy. The animation lags behind the actual movement of the view. The button is already at the destination point when the animation starts. You just see a movie of the view/button moving. If you want a button to be clickable during the animation, you'll have to make the animation yourself.
So the coe would be like this-
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint location = [t locationInView:self.view];
for (UIButton *button in self.buttonsOutletCollection)
{
if ([button.layer.presentationLayer hitTest:location])
{
// This button was hit whilst moving - do something with it here
break;
}
}
}

Related

How do I change an animation for an object while it is already animating?

So, right now I have a rocket which bounces side to side across the bottom of the iphone 5 screen. I want the rocket however to stop moving side to side but to go upwards once the user touches the screen (so touching the screen makes the rocket take off).
First, in viewDidAppear I told the compiler to run the spawnRocket method after 2 seconds.
In the spawnRocket method I established the rockets image and frame, and then I added it to the view and performed a uiview animation sending the rocket to the right side of the screen. In the finished parameter in the uiview animation method I told the compiler to run the moveRocketLeft method.
In the moveRocketLeft method I did an animation which would send the rocket to the left of the screen, and in the finished parameter I ran the moveRocketRight method.
The moveRocketMethod is basically the spawnRocket method except I don't establish the rockets image and frame and add it to the view.
So after all this, I tried to implemented the -touchesBegan:withEvent: method. I tried simply running a uiview animation which changed the rockets y to off the screen upwards, and the x to whatever the x was currently at when the user touched the screen.
However, I realize that the calling the rockets frame does not return the location that the rocket looks like while animating. It really just returns what the rockets frame will be when its done animating. So, to get the frame I want should I call layer? I remember reading somewhere that layer will return the actual location of a uiimageview during an animation.
But, layer only has the property bounds and not frame, so I'm a little confused.
I also tried calling [self.view.layer removeAllAnimations]; and then running a new animation which would shoot the awkward up, but the removeAllAnimations method never worked (and anyway i don't really like the idea of using that because i heard it lags).
So anyone have any idea how i can implement touchesBegan:withEvent: method so that the rocket can take off correctly? (or if you think I have to change my whole program please feel free to help)
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property UIImageView *rocket;
#end
#implementation ViewController
#synthesize rocket=_rocket;
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.view.layer removeAllAnimations];
// [UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^(){self.rocket.frame=CGRectMake(self.rocket.frame.origin.x, -40, 25, 40);} completion:^(BOOL finished){}];
// this didn't work :(
}
-(void)viewDidAppear:(BOOL)animated{
[self performSelector:#selector(spawnRocket) withObject:self afterDelay:2];
}
-(void)spawnRocket{
self.rocket=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"default.png"]]; //places imageview right off screen
self.rocket.frame=CGRectMake(-25, 420, 25, 40);
[self.view addSubview:self.rocket];
[UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionCurveLinear animations:^(){self.rocket.frame=CGRectMake(295, 420, 25, 40);} completion:^(BOOL finished){if (finished)[self moveRocketLeft]; }];
}
-(void) moveRocketLeft{
[UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionCurveLinear animations:^(){self.rocket.frame=CGRectMake(0, 420, 25, 40);} completion:^(BOOL finished){if (finished)[self moveRocketRight];}];
}
-(void)moveRocketRight{
[UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionCurveLinear animations:^(){self.rocket.frame=CGRectMake(295, 420, 25, 40);} completion:^(BOOL finished){if (finished)[self moveRocketLeft];}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This should do the trick
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
CALayer *rocketPresentationLayer = [self.rocket.layer presentationLayer];
self.rocket.layer.position = rocketPresentationLayer.position;
[UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionBeginFromCurrentState animations:^(){self.rocket.frame=CGRectMake(self.rocket.frame.origin.x, -40, 25, 40);} completion:^(BOOL finished){}];
}
Note 1: I didn't put logic in here to check whether the user tapped the rocket again, when it is taking off vertically. You might want to add a BOOL or something to see if the user successfully hit the rocket or not, and if so, don't perform the vertical animation again.
Note 2: If in future you only want this to occur when the rocket is hit, you can use hitTest checking to call the animation conditionally.
// Get the touch
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
// See if we hit the rocket
CALayer *rocketPresentationLayer = [self.rocket.layer presentationLayer];
CALayer* hitTest = [rocketPresentationLayer hitTest:touchPoint];
// If we did, then stop animation and move upwards
if (hitTest) {
....
}
P.S: Also as a slight mention, #property UIImageView *rocket; should be changed to #property (nonatomic) UIImageView *rocket;. This is primarily for performance reasons, as I don't expect your rocket to be accessed by multiple threads (it shouldn't, it's the UI!).

Words falling from the top of screen in iphone

Im developing a word finding app.Here i want some words to fall from the top of screen of iphone.
There will be a red line at the bottom of screen.
User have to select the correct word before reaching a red line.
So how can i implement the falling effect in iphone.(i.e words falling effect).
Can anyone please help me with this.
Thanks in advance.
You can implement this using UIView animations. This sample code is all within a view controller, with a single label, fallingLabel added to the view. You can expand it to include multiple labels.
The key point is to import the QuartzCore framework. This gives you access to the presentationLayer of a view, which holds the current state of an animating view. You add the framework to your project in the project summary part of Xcode, then put the following in the top of your view controller's implementation file:
#import <QuartzCore/QuartzCore.h>
The following code animates the dropping of the label. It will increase in speed as it falls due to the UIViewAnimationOptionCurveEaseIn setting, giving you a convincing gravity effect.
self.fallingLabel.center = CGPointMake(160,50);
[UIView animateWithDuration:3
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{self.fallingLabel.center = CGPointMake(160,400);}
completion:nil];
Now, you need to implement touch handling. This is not done on the label, as internally it will think it is already at the bottom of the screen. You handle the touches in your view controller:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if ([self.fallingLabel.layer.presentationLayer hitTest:touchLocation])
{
NSLog(#"Label was touched");
CALayer *presLayer = self.fallingLabel.layer.presentationLayer;
[UIView animateWithDuration:0
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{self.fallingLabel.frame = presLayer.frame;}
completion:nil];
}
}
Here, what is happening is that the presentation layer is being queried to see if the touch falls within it's current bounds. If so, we then start a new animation, which starts from the current state - in this case all I have done is stop the falling of the label, but you could implement anything you like here.
If you have multiple falling labels you can try the hitTest on the presentation layer of each one until you find the one that has been touched.
try to use usual animations blocks, but run than in separate theard:
NSOperationQueue *_queue;
_queue = [[NSOperationQueue alloc] init];
SEL selector = #selector(fallWord);
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:signature];
[inv setTarget:self];
[inv setSelector:selector];
NSInvocationOperation *fall= [[[NSInvocationOperation alloc] initWithInvocation:inv] autorelease];
[_queue addOperation:fall];
then, implement fallWord method, where you will set an animatin of falling:
UILabel *lb = [[UILabel alloc] initWithFrame:CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>)];
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
[lb setCenter:CGPointMake(<#CGFloat x#>, <#CGFloat y#>)];
} completion:nil];
then, you will need to implement touches and in method
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSUInteger touchCount = 0;
for (UITouch *touch in touches) {
[self dispatchFirstTouchAtPoint:[touch locationInView:self] forEvent:nil];
touchCount++;
}
}
check if touch coodinates inside your UILabel's frame
Cocos2d combined with box2d or chipmunk will help you achieve a falling affect (simulating gravity towards the bottom of the screen).
I will give a general plan with Cocos2d / chipmunk
Learn both.
Cocos2d - CCLabelTTF, CCLayer, CCSprite, CCTouch recognition, how to schedule a timer with cocos2d
Chipmunk - bodies, gravity, using the timer set up with cocos2d to step through the physics, calling a C static void function to update the CCLabelTTF's that are still on screen, removing those objects that aren't on screen (past the red line), and how to use the Cocos2d touch methods to verify if you hit a word or not.
ie method
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
There are great starter documents on Cocos2d's site and not so great documentation for chipmunk, but you can find tutorials on hooking that in.
I am not sure if UIKit animations can be interrupted how they will need to be in your app.
In any implementation, you will also need to ensure your view displaying the text isn't bigger than the text.
If you take the cocos2d + physics engine route to achieve your goal, you will be in good shape to learn how to make an even more complex game.
You are actually looking for AutoComplete textbox. Basically there is no such thing exist in iPhone. but with the help of UIPicker view you can provide such effect

Detect if a touch is on a moving UIImageView from a View Controller

I have a UIViewController that is detecting touch events with touchesBegan. There are moving UIImageView objects that float around the screen, and I need to see if the touch event landed on one of them. What I am trying:
UITouch* touch = [touches anyObject];
if ([arrayOfUIImageViewsOnScreen containsObject: [touch view]]) {
NSLog(#"UIImageView Touched!");
}
But this never happens. Also if I were to do something like this:
int h = [touch view].bounds.size.height;
NSLog([NSString stringWithFormat: #"%d", h]);
it outputs the height of the entire UIViewController (screen) everytime, even if I touch one of the UIImageViews, so clearly [touch view] is not giving me the UIImageView. How do I detect when only a UIImageView is pressed? Please do not suggest using UIButtons.
Thank you!
If you only want to detect when a UIImageView is pressed, check the class:
if (touch.view.class == [UIImageView class]) {
//do whatever
}
else {
//isnt a UIImageView so do whatever else
}
Edit----
You haven't set the userInteraction to enabled for the UIImageView have you?!
I know you said please do not suggest using UIButtons, but buttons sound like the best/easiest way to me.
You could try sending the hitTest message to the main view's CALayer with one of the touches - it'll return the CALayer furthest down the subview hierarchy that you touched.
You could then test to see if the layer you touched is a layer of one of the UIImageView's, and proceed from there.
This code uses a point generated from a UIGestureRecognizer.
CGPoint thePoint = [r locationInView:self.view];
thePoint = [self.view.layer convertPoint:thePoint toLayer:self.view.layer.superlayer];
selectedLayer = [self.view.layer hitTest:thePoint];
If you want to check the touch means use CGRectContainsPoint.
1.Capture the touch event and get the point where you touched,
2.Make a CGRect which bounds the object you want to check the touch event,
3.Use CGRectContainsPoint(CGRect , CGPoint) and catch the boolean return value.
http://developer.apple.com/library/ios/#DOCUMENTATION/GraphicsImaging/Reference/CGGeometry/Reference/reference.html
Here is the class reference for CGRect.
Forgot about this question- the problem was that I did not wait until viewDidLoad to set userInteractionEnabled on my UIImageView.

Iphone SDK: Image arrays?

I'm making a "Tapping-App" where you have to kill zombies by tapping on them. Currently i can't make any progress because i can't use the methods i need properly.
My App works like that:
I have a timer that spawns an image 3 times per second:
[NSTimer scheduledTimerWithTimeInterval:1.0/3
target:self
selector:#selector(Spawn)
userInfo:nil
repeats:YES];
then i have the Spawn command:
- (void) Spawn {
CGRect myImageRect = CGRectMake(0.0f, 0.0f, 320.0f, 109.0f);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"myImage.png"]];
myImage.opaque = YES;
[self.view addSubview:myImage];
[myImage release]
}
I also have a "TouchesBegan" command:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
}
What I want to do, is put all the spawning images into an Array; If one of the images is touched i want it to be removed from the superview.I want to know how an array works and how i can remove objects added to the array from a different function like "TouchesBegan".
Please help me with my problem!
EDIT:
Nevermind... I searched around the internet and found some useful resources witch were almost what i needed. I finally figured out how it works and got my spawner function to work with collision and with "TouchesBegan".
If somebody wants the code, just ask me.
DD
Use [NSMUtableArray addObject:] and [NSMutableArray removeObject:]
In your interface, something like:
#interface MyClass
{
NSMutableArray *zombies_;
}
In your implementation, after initializing the array, change your spawn method to include the line
[self.zombies addObject:myImage];
and in your touch handler, after you determine which view was touched, something like
[zombies_ removeObject:theView];
[theView removeFromSuperview];
Although, after thinking about it, you may be better off just creating UIButton instances instead of UIIMageViews and writing touch handlers to determine which view was touched.

Is it possible to successful animate a moving UIButton?

I'm trying to animate a button which moves around the screen. At any point, the user can push the button. But the button doesn't respond to touches. I've tried an animation block but the button simply moves immediately to its end coordinates, while the frame shows the animation (the button is called bubble):
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:5.0];
CGAffineTransform newTransform = CGAffineTransformMakeScale(3, 3);
bubble.transform = CGAffineTransformTranslate(newTransform, 0, -460);
[UIView commitAnimations];
So then I tried Core Animation with the help of some sample code (the path isn't important, it's just an example):
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CAKeyframeAnimation *theAnimation=[CAKeyframeAnimation animationWithKeyPath:#"position"];
theAnimation.path=thePath;
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.animations=[NSArray arrayWithObject:theAnimation];
theGroup.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
theGroup.duration=15.0;
CFRelease(thePath);
[bubble.layer addAnimation:theGroup forKey:#"animatePosition"];
But the buttons still don't respond to touches. Btw, I have several of these 'bubble' buttons on screen at once so having several NSTimers concurrently active wouldn't be optimal.
Can anyone suggest another approach? Should I perhaps animate UIImageViews and make them repsond to touches? Or will I have the same problem? This has been puzzling me for a couple of days so any help much appreciated.
Thanks :)
Michael
When animating a GUI element like the UIButton, the actual position only changes when the animation is done. This means any touch events during the animation will only work at the starting point of the button. You can get the current displayed position of the button by accessing it's presentationLayer. You'll probably need to implement your own event handling, look at the position when the user touches the screen, and compare that to the bounds you get from the presentationLayer.
Well, for anyone interested, here's my solution which works perfectly well:
- (void)startMinigame {
makeBubbles = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(generateRandomBubble) userInfo:nil repeats:YES];
floatBubbles = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:#selector(floatBubbles) userInfo:nil repeats:YES];
}
- (void)generateRandomBubble {
//Get a random image for the bubble
int randomIndex = arc4random()%kNumberBubbles;
UIImage *bubbleImage = [bubbleImages objectAtIndex:randomIndex];
//Create a new bubble object and retrieve the UIButton
Bubble *bubble = [[Bubble alloc]initWithImage:bubbleImage andIndex:randomIndex];
UIButton *bubbleButton = bubble.bubbleButton;
//Configure the button
[bubbleButton addTarget:self action:#selector(bubbleBurst:) forControlEvents:UIControlEventTouchDown];
//Add the bubble to the scene
[self.view insertSubview:bubbleButton belowSubview:bathTub];
//Add the bubble to an array of currently active bubbles:
[self.bubbles addObject:bubble];
[bubble release];
}
- (void)floatBubbles {
//Move every active bubble's frame according to its speed.
for (int i = 0; i < [bubbles count]; ++i) {
Bubble *bubble = [bubbles objectAtIndex:i];
int bubbleSpeed = bubble.speed;
CGRect oldFrame = bubble.bubbleButton.frame;
CGRect newFrame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y - bubbleSpeed, oldFrame.size.width+0.1, oldFrame.size.height+0.1);
bubble.bubbleButton.frame = newFrame;
if (bubble.bubbleButton.frame.origin.y < -100.0) {
[bubbles removeObject:bubble];
[bubble.bubbleButton removeFromSuperview];
}
}
}
This sounds a bit too complicated for UIKit and CoreAnimation. It seems like you're developing something like a game. Why not use a global animation timer for let's say ~60 fps and do your drawing in Quartz2D? Then for each touch you could quickly check any hits between Quartz refreshes.
Try animating the uiview's frame property.