-2

I'm currently taking my first shaky steps in ios development. I'm trying to animate a free falling ball. I have a button connected to an IBAction, and and UIImageView containing an image of my ball.

Inside my action, I have a while loop that's timed using NSTimeInterval, and based on the time it takes it calculates a new position until the ball reaches 'the ground'(Yes, I realize this is probably the least optimal way to do it. But I'm having a hard time grasping the syntax (and therein my problem probably lays), the optimisation will have to come later). From what I can understand, NSTimeInterval returns the elapsed time in seconds, so even though it will increment incredibly small steps, it should work. But I may have a serious case of brain fart.

So far so good. But when I tap the button, the ball moves straight from it's starting point to it's finishing point without an animation.

-(IBAction)doshit:(id)sender{

int G = 10;
CGPoint center = [myImage center];
NSDate *startTime = [NSDate date];
NSTimeInterval T;

T = fabs([startTime timeIntervalSinceNow]);
while (center.y<480)
{

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:T];

    center.y=center.y+((G*T*T)/2);
    [myImage setCenter:center];

    T = fabs([startTime timeIntervalSinceNow]);
    [UIView commitAnimations];
}

}

I welcome all suggestions! =)

  • 1
    You can't loop through a series of values and expect to see the intermediate ones, because drawing is done on the run loop and you must allow your method to return (i.e. not block) in order for drawing to update. So that's why you're seeing it jump. I'm sorry I don't know a good solution though. – Carl Veazey Mar 26 '13 at 01:17

2 Answers2

0

One way to do it is to use a CADisplayLink to provide the timing -- it is tied to the display refresh rate of 1/60 of a second. If you 're using auto layout (which is on by default now), it is better to animate a constraint, rather then set a frame. So, this example uses a button at the top of the screen, whose constraint to the top of the view is connected to the IBOutlet topCon.

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (weak,nonatomic) IBOutlet NSLayoutConstraint *topCon;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self performSelector:@selector(startDisplayLink) withObject:nil afterDelay:2];
}

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

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

- (void)handleDisplayLink:(CADisplayLink *)displayLink {
    static BOOL first = YES;
    static double startTime = 0;
    if (first) {
        startTime = displayLink.timestamp;
    }
    first = NO;
    double T = (double)displayLink.timestamp - startTime;
    self.topCon.constant += ((10 * T * T)/2);
    if (self.topCon.constant > 420) {
        [self stopDisplayLink];
    }
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Thank's for your reply, but that went straight over my head. I'm fairly inexperienced in programming in general, and this object orienting is making my head spin. I think I'm better of taking a few steps back and starting from scratch. – KaptenFrans Mar 26 '13 at 19:10
0

As Carl notes, you cannot perform animations by repeatedly changing things in the middle of a method call. See What is the most robust way to force a UIView to redraw? for more discussion on that.

As you may suspect, not only is this non-optimal, it actively fights iOS. iOS has many easy-to-use techniques for performing smooth animations without resorting to timers of any kind. Here is one simple approach:

- (IBAction)dropAnimate:(id)sender {
  [UIView animateWithDuration:3 animations:^{
     self.circleView.center = CGPointMake(100, 300);
   }];
}

In the animations block, you change the thing you want to change to the final value (myImage.center in your case). UIKit will sample the current value and the final value, and figure out a path to get you there in the time you requested. For a full, runnable example with a few other features (like chained animations), see the example code from iOS:PTL Chapter 9.

The above code will use the default timing function. Once you understand that, you can move on to customizing the timing function. See Animation Pacing in the Animation Types and Timing Programming Guide. Also How to create custom easing function with Core Animation? and Parametric acceleration curves in Core Animation. But I would get your head around simple, default animations first.

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • (Apologies for the late reply, probably different time zones.) Oh, I see. Kind of. Thank you for clearing that up. I'm starting to realize that this is a tad bit more complex than I think it is. I think I better go back a few steps and get the hang on some static stuff before I continue with dynamics. Thank you very much for your reply! – KaptenFrans Mar 26 '13 at 19:08