animating in 3 steps one after the other ios - ios

I have 3 cards. at first all cards move to the right, then after 1 sec card2 would appear on the left side of the view, and then after some more second card2 moves to the right. I implemented this using the following code. when the view loads I have the following code,
counter = 0;
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(performAnimationToRight) userInfo:nil repeats:YES];
here is the implementation of the performAnimationToRightMethod
-(void)performAnimationToRight
{
if (counter == 0) {
CGAffineTransform transformToLeft = CGAffineTransformMakeTranslation(388,0);
[UIView beginAnimations:#"Move1" context:nil];
[UIView setAnimationDuration:2];
card0.transform = transformToLeft;
card1.transform = transformToLeft;
card2.transform = transformToLeft;
[UIView commitAnimations];
}else if(counter == 50){
card2.frame = CGRectMake(-300,card2.frame.origin.y, card2.frame.size.width, card2.frame.size.height);
}else if(counter == 200){
timer = nil;
counter = 0;
CGAffineTransform transformToLeft = CGAffineTransformMakeTranslation(-300,0);
[UIView beginAnimations:#"Move2" context:nil];
[UIView setAnimationDuration:2];
card2.transform = transformToLeft;
[UIView commitAnimations];
}
counter++;
}
the problem is that on the last action card2 instead of moving to the right moves to the left, and that's wether or not I use,
CGAffineTransform transformToLeft = CGAffineTransformMakeTranslation(-300,0);
or
CGAffineTransform transformToLeft = CGAffineTransformMakeTranslation(+300,0);

Two comments:
First, you are moving your card2 to the left at counter == 50 by setting it's frame origin to be (probably) left of your starting position.
Second, this would be easier to implement using a CAKeyframeAnimation. You wouldn't have to manage your NSTimer and the counter mechanism.

Related

How do I make a bounce effect after slide effect in xcode?

I have this code that can slide to left if swiped
[UIView animateWithDuration:1.0 delay:2.0 options:(UIViewAnimationCurveEaseInOut|UIViewAnimationOptionAllowUserInteraction)
animations:^
{
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(faceRight:finished:context:)];
self.bug.center = CGPointMake(75, 200);
}
completion:^(BOOL finished)
{
NSLog(#"Move to left done");
}
];
but I want to add 1 bounce effect, maybe add a code like:
self.bug.center = CGPointMake(78, 200);
but I dont know how to add this code, where should I write this? I'm new to animations. please help :)
-(void)onTimer
{
ball.center = CGPointMake(ball.center.x+pos.x,ball.center.y+pos.y);
if(ball.center.x > 320 || ball.center.x < 0)
pos.x = -pos.x;
if(ball.center.y > 460 || ball.center.y < 0)
pos.y = -pos.y;
}
-(your method)
{
pos = CGPointMake(14.0,7.0);
[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
}
You should use the block based animation methods, it says in the documentation that using blocks is the recommended way. Also, in iOS 7, UIView has a new +method that will do just that. Or you can use the UIkit Dynamics system.

UIView Animation doesn't run in correct order in for loop - iOS

I have a simple for loop which animates some UILabels. The reason I have the animation block code in my for loop is because obviously I have more than one label I am trying to animate.
Now my program increments the numbers in the UILabels by one every time AFTER the ENTIRE for loop has finished. However by the time the animation happens, the number has already been incremented (which is not what I want), even though the number increment code comes AFTER the for loop....
I'm not sure if I have explained the problem correctly, but in ensconce what I am asking, since we know for a FACT that animations run on the main thread, how can we ensure that the animations happen in the correct order (while in a for loop)???
Here is my code:
for (int loop = 0; loop < [number_arrange count]; loop++) {
if ([number_arrange[loop] integerValue] == checknum) {
[anim_check replaceObjectAtIndex:loop withObject:[NSNumber numberWithInteger:1]];
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
((UILabel*)_labels_anim[loop]).transform = CGAffineTransformMakeScale(5.0, 5.0);
((UILabel*)_labels_anim[loop]).layer.anchorPoint = CGPointMake(0.5, 0.5);
((UILabel*)_labels_anim[loop]).frame = CGRectOffset(((UILabel*)_labels_anim[loop]).frame, 0.0, -20.0);
((UILabel*)_labels_anim[loop]).alpha = 0.0;
} completion:^(BOOL finished) {
((UILabel*)_labels_anim[loop]).transform = CGAffineTransformMakeScale(1.0, 1.0);
((UILabel*)_labels_anim[loop]).layer.anchorPoint = CGPointMake(0.5, 0.5);
((UILabel*)_labels_anim[loop]).frame = CGRectOffset(((UILabel*)_labels_anim[loop]).frame, 0.0, 20.0);
((UILabel*)_labels_anim[loop]).alpha = 1.0;
}];
}
}
Thanks for your time, Dan.
The issue is that you're accessing "loop" from inside both blocks. Why don't you try running the loop inside of the animation blocks? Something like this:
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
for (int loop = 0; loop < [number_arrange count]; loop++) {
if ([number_arrange[loop] integerValue] == checknum) {
[anim_check replaceObjectAtIndex:loop withObject:[NSNumber numberWithInteger:1]];
((UILabel*)_labels_anim[loop]).transform = CGAffineTransformMakeScale(5.0, 5.0);
((UILabel*)_labels_anim[loop]).layer.anchorPoint = CGPointMake(0.5, 0.5);
((UILabel*)_labels_anim[loop]).frame = CGRectOffset(((UILabel*)_labels_anim[loop]).frame, 0.0, -20.0);
((UILabel*)_labels_anim[loop]).alpha = 0.0;
}
}
} completion:^(BOOL finished) {
for (int loop = 0; loop < [number_arrange count]; loop++) {
if ([number_arrange[loop] integerValue] == checknum) {
((UILabel*)_labels_anim[loop]).transform = CGAffineTransformMakeScale(1.0, 1.0);
((UILabel*)_labels_anim[loop]).layer.anchorPoint = CGPointMake(0.5, 0.5);
((UILabel*)_labels_anim[loop]).frame = CGRectOffset(((UILabel*)_labels_anim[loop]).frame, 0.0, 20.0);
((UILabel*)_labels_anim[loop]).alpha = 1.0;
}
}
}];
I hope that helps!

Why are these UIImages glitching when they reload on the screen?:

In my code, after the frog jumps on the lily pad, the lilypad fades out slowly, (making it look like they are submerged) and then after they pass the bottom of the screen, they reload on the top of the screen. The code is as follows:
if (CGRectIntersectsRect(FrogSquare.frame, lilypadTS.frame) && (upMovement <= -1) && (swim == YES) && (startingice.hidden == NO)){
[self bounce];
[self iceblockfall];
if (lilypadused == NO) {
addedScore = 1;
lilypadused = YES;
}
//hide pad
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:4];
[startingice setAlpha:0];
[UIView commitAnimations];
[self performSelector:#selector(hidePad) withObject:self afterDelay:4.0];
}
That part works fine, the lily pad does what its supposed to do. The following is where I get this weird glitch:
if (lilypad.center.y > 610) {
RandomPosition = arc4random() %248;
RandomPosition = RandomPosition + 36;
lilypad.center = CGPointMake(RandomPosition, -22);
lilypadused = NO;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0];
[lilypad setAlpha:1];
[UIView commitAnimations];
[self performSelector:#selector(showlilypad) withObject:self afterDelay:0];
} else if ((lilypad.center.y > 610) && (lilypad.hidden == YES)) {
RandomPosition = arc4random() %248;
RandomPosition = RandomPosition + 36;
lilypad.center = CGPointMake(RandomPosition, -22);
lilypadused = NO;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0];
[lilypad setAlpha:1];
[UIView commitAnimations];
[self performSelector:#selector(showlilypad) withObject:self afterDelay:0];
}
What happens is for some reason, the lilypads will reload at the top of the screen like they're supposed to, but then as the user is jumping on lilypads below the ones at the top. If thats confusing -- Screen Dimensions 320 x 568, lilypad1 reloads at the top of the screen point = -22, then slowly falls down, so say point is 10 after falling for a bit, say the player jumps on lilypad2 that HAS NOT been hidden and gone past 610 (to reload at the top), it makes lilypad1 hide instantly, why is this??
The amount of code you've provided really isn't enough to diagnose your problem. You are likely (definitely) referencing the incorrect lilypad which is causing the errant behavior. You should probably take a close look at the architecture of your app to make sure you are properly managing the list of lilypads in the best way.
Also, whenever you use delays - note that those delays are approximate, not exact. It is possible that you are in a race condition, where the hide has yet to occur but you're already showing it again at the top, then the hide triggers later.
Lastly, if afterDelay is 0 you might as well just call the showlilypad method directly - no need to really delay for 0 seconds.
Good luck!

Animation with images when I click on it

I would like that when I click on the image, another image appears for 1 or 2 seconds.
I have two images (UIImage), one for pressed and another for unpressed.
Is it something like this? :
[UIView animateWithDuration: 0.2 delay: 0.0 options: UIViewAnimationOptionAutoreverse animations:^{
pressed.alpha = 1.0;
unPressed.alpha = 0.0;
}completion:^(BOOL finished){
pressed.alpha = 0.0;
unPressed.alpha = 1.0;
}];
But with that I can't see the pressed image.
Edit: My problem is to asign the image to the UIImageView I think, because when I click on it appear the pressed image but then the unPressed image not appear
I would like any similar to that:
[UIView animateWithDuration: 2.0 delay: 0.0 options: UIViewAnimationOptionAutoreverse animations:^{
pressed.alpha = 1.0;
unPressed.alpha = 0.0;
self.imageButton.image = pressed.image;
}completion:^(BOOL finished){
pressed.alpha = 0.0;
unPressed.alpha = 1.0;
self.imageButton.image = unPressed.image;
}];
First off, you set the animation duration to .2 so you may want to increase the duration to 1.0 or 2.0 if you in fact want it to show for 1 or 2 seconds.
Secondly, I'm assuming these images overlap one another, so when both alphas are 1, still only one of the images will be showing. Make sure unPressed is hidden before starting the animation; then make sure pressed is hidden after ending the animation.
unPressed.alpha = 0;
[UIView animateWithDuration: 2.0 delay: 0.0 options: UIViewAnimationOptionAutoreverse animations:^{
pressed.alpha = 1.0;
} completion:^(BOOL finished){
pulsando.alpha = 0.0;
pressed.alpha = 0.0;
unPressed.alpha = 1.0;
}];
(And one more question for you… You said you wanted the image to appear for 1 or 2 seconds. Did you want the image to just appear? Or did you want it to fade in? Just double checking… Because if you just want it to appear, you don't need the animation block.)
Edit: (In response to your edit)
Seems as if you don't need an animation block at all and that you're just trying to have the image appear for 2 seconds. In which case, when the button is pressed, show the image then hide the image after 2 seconds. For example:
- (IBAction)buttonSelected:(UIButton*)sender {
pressed.alpha = 1.0;
unPressed.alpha = 0.0;
[self performSelector:#selector(hidePressedImage) withObject:nil afterDelay:2.0];
}
- (void)hidePressedImage {
pressed.alpha = 0.0;
unPressed.alpha = 1.0;
}
Two images but three variables. Every time you run this function you will see the unPressed image + pressed image (and you will see the one that was added to the view at a later time, since it will be in front).
Maybe pressed image is overlapped by normal? Try following:
[UIView animateWithDuration: 0.2 delay: 0.0 options: UIViewAnimationOptionAutoreverse animations:^{
pressed.alpha = 1.0;
unPressed.alpha = 0.0;
}completion:^(BOOL finished){
pulsando.alpha = 0.0;
unPressed.alpha = 1.0;
pressed.alpha = 0.0;
}];

Grow a UIImageView while UILongPressGestureRecognizer active

I want to add an UIImageView to my view at the users touch location and have the UIImageView grow while the user is holding their finger down. Think of a ballon being blown up. I want the center of the UIImageView to remain at the user's touch location while its growing.
I figured the best way would be a UILongPressGestureRecognizer and wrote the below. This does work as I planed except the visual effect is somewhat choppy and clumsy.
Is there any way that I can animate the UIImageView's size until the UILongPressGestureRecognizer calls UIGestureRecognizerStateEnded?
Or, is there a better way to do this altogether?
declared in .h: CGPoint longPressLocation;
.m:
- (IBAction) handleInflation:(UILongPressGestureRecognizer *) inflateGesture {
longPressLocation= [inflateGesture locationInView:self.view];
switch (inflateGesture.state) {
case UIGestureRecognizerStateBegan:{
NSLog(#"Long press Began .................");
inflateTimer = [NSTimer scheduledTimerWithTimeInterval:.4 target:self selector:#selector(inflate) userInfo:nil repeats:YES];
UIImage *tempImage=[UIImage imageNamed:#"bomb.png"];
UIImageView *inflatableImageView = [[UIImageView alloc] initWithFrame:CGRectMake(longPressLocation.x-tempImage.size.width/2,
longPressLocation.y-tempImage.size.height/2,
tempImage.size.width, tempImage.size.height)];
inflatableImageView.image = tempImage;
[bonusGame addSubview:inflatableImageView];
inflatable=inflatableImageView;
}
break;
case UIGestureRecognizerStateChanged:{
NSLog(#"Long press Changed .................");
}
break;
case UIGestureRecognizerStateEnded:{
NSLog(#"Long press Ended .................");
[inflateTimer invalidate];
}
break;
default:
break;
}
}
-(void)inflate{
inflatable.frame=CGRectMake(inflatable.frame.origin.x,inflatable.frame.origin.y , inflatable.bounds.size.width+15, inflatable.bounds.size.height+15);
inflatable.center=longPressLocation;
}
Final Working Code:
- (IBAction) handleInflation:(UILongPressGestureRecognizer *) inflateGesture {
inflateGesture.minimumPressDuration = .01;
longPressLocation= [inflateGesture locationInView:self.view];
switch (inflateGesture.state) {
case UIGestureRecognizerStateBegan:{
NSLog(#"Long press Began .................");
inflateStart = [NSDate date];
inflateDisplayLink = [NSClassFromString(#"CADisplayLink") displayLinkWithTarget:self selector:#selector(inflate)];
[inflateDisplayLink setFrameInterval:1];
[inflateDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
UIImage *tempImage=[UIImage imageNamed:#"bomb.png"];
UIImageView *inflatableImageView = [[UIImageView alloc] initWithFrame:CGRectMake(longPressLocation.x-tempImage.size.width/2,
longPressLocation.y-tempImage.size.height/2,
tempImage.size.width, tempImage.size.height)];
inflatableImageView.image = tempImage;
[bonusGame addSubview:inflatableImageView];
inflatable=inflatableImageView;
}
break;
case UIGestureRecognizerStateChanged:{
NSLog(#"Long press Changed .................");
}
break;
case UIGestureRecognizerStateEnded:{
NSLog(#"Long press Ended .................");
[inflateDisplayLink invalidate];
}
break;
default:
break;
}
}
-(void)inflate{
NSDate *inflateEnd = [NSDate date];
NSTimeInterval inflateInterval;
inflateInterval = ([inflateEnd timeIntervalSince1970] - [inflateStart timeIntervalSince1970])*25;
inflatable.frame=CGRectMake(inflatable.frame.origin.x,
inflatable.frame.origin.y ,
inflatable.bounds.size.width+inflateInterval,
inflatable.bounds.size.height+inflateInterval);
inflatable.center=longPressLocation;
if(inflatable.bounds.size.width>200){
[inflateDisplayLink invalidate];
}
}
A timer may not be smooth. Instead check out CADisplayLink, which will provide a delegate callback whenever the device's screen is redrawn (~60hz), so you will get a per frame chance to adjust your balloon size.
Another thing to consider is the time between refreshes isn't constant, it could be a lot slower than when the last refresh occured, so if you are incrementing the size by a constant 15 every time you get a callback, then the animation may not seem smooth.
To combat this, when you start the animation take a timestamp and hold onto it, then when you inflate the balloon take another timestamp and determine the difference between now and the last redraw, then multiply the difference by some value, which will ensure a constant smooth size growth - this is called a timestep.
https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/Reference/Reference.html
Getting creative here, but I think this might work:
You start an animation that animates the images frame from its current size towards a maximum size.
Start the animation as soon as you detect the long press gesture.
If the gesture ends, get the current frame size from the presentationLayer of the animated view/image and update the view/image's frame to that size by starting a new animation with UIViewAnimationOptionBeginFromCurrentState so that the old animation will stop.
That should give you a smoothly growing balloon.
Try this....
imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(120, 100, 30, 30))];
imageView.backgroundColor = [UIColor redColor];
[self.view addSubview:imageView];
imageView.userInteractionEnabled = TRUE;
UILongPressGestureRecognizer *g = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[g setMinimumPressDuration:0.001];
[imageView addGestureRecognizer:g];
And Add these methods
-(void)longPress:(UITapGestureRecognizer *)t
{
if (t.state == UIGestureRecognizerStateBegan)
{
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeIncrc) userInfo:nil repeats:TRUE];
}
else if (t.state == UIGestureRecognizerStateEnded || t.state == UIGestureRecognizerStateCancelled)
{
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeDec) userInfo:nil repeats:TRUE];
}
}
-(void)makeIncrc
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:(UIViewAnimationCurveLinear)];
CGRect frame = imageView.frame;
frame.origin.x = frame.origin.x - 5;
frame.origin.y = frame.origin.y - 5;
frame.size.width = frame.size.width + 10;
frame.size.height = frame.size.height + 10;
imageView.frame = frame;
[UIView commitAnimations];
}
-(void)makeDec
{
if (imageView.frame.size.width < 20 || imageView.frame.size.height < 20)
{
[timer invalidate];
}
else
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:(UIViewAnimationCurveLinear)];
CGRect frame = imageView.frame;
frame.origin.x = frame.origin.x + 5;
frame.origin.y = frame.origin.y + 5;
frame.size.width = frame.size.width - 10;
frame.size.height = frame.size.height - 10;
imageView.frame = frame;
[UIView commitAnimations];
}
}
Here's a Swift version. I had to replace invalidate() calls with "paused" in order to avoid runaway inflation.
var inflateDisplayLink: CADisplayLink?
var inflateStart: NSDate!
var longPressLocation: CGPoint!
var inflatable: UIImageView!
func handleInflation(inflateGesture: UILongPressGestureRecognizer) {
longPressLocation = inflateGesture.locationInView(self.view)
let imageView = inflateGesture.view as! UIImageView
switch (inflateGesture.state) {
case .Began:
println("Long press Began .................")
inflateStart = NSDate()
let tempImageView = UIImageView(image: imageView.image)
tempImageView.contentMode = .ScaleAspectFit
tempImageView.frame = imageView.frame
self.view.addSubview(tempImageView)
inflatable = tempImageView
if inflateDisplayLink == nil {
inflateDisplayLink = CADisplayLink(target: self, selector: Selector("inflate"))
inflateDisplayLink!.frameInterval = 1
inflateDisplayLink!.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
} else {
inflateDisplayLink!.paused = false
}
break
case .Changed:
println("Long press Changed .................")
break
case .Ended:
println("Long press Ended .................")
inflateDisplayLink!.paused = true
break
default:
break
}
}
func inflate() {
var inflateEnd = NSDate()
// Convert from Double to CGFloat, due to bug (?) on iPhone5
var inflateInterval: CGFloat = CGFloat((Double(inflateEnd.timeIntervalSince1970) - Double(inflateStart.timeIntervalSince1970))) * 5.0
inflatable.frame = CGRectMake(inflatable.frame.origin.x, inflatable.frame.origin.y, inflatable.bounds.size.width + inflateInterval, inflatable.bounds.size.height + inflateInterval)
inflatable.center = longPressLocation.center
if inflatable.bounds.size.width > 200 {
inflateDisplayLink!.paused = true
}
}

Resources