0

Background: I am using xcode 3.1.4 and iphone simulator 3.1 (I know it's outdated and please dont comment about that).

Goal: I am trying to get a UIImageView that is created once the view loads to continuously move down. Creating the UIImageView works fine, but the move function does not work. Nothing happens. I use an NSTimer. Here is my code in the view controller.m file:

-(void)viewDidLoad{
    UIImageView *six = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"Meteor.png"]];
    CGRect rectSix = CGRectMake(arc4random() % (250), arc4random() % (1), 35, 35);
    [six setFrame:rectSix];
    [self.view addSubview:six];
    [self moveMeteor:six];
    [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(moveMeteor:) userInfo:six repeats:YES];

}

-(void)moveMeteor:(UIImageView *)t{

    t.center=CGPointMake(t.center.x, t.center.y + 1); 


}

And of course, I declared my moveMeteor function in the view controller.h file. By typing in:

-(void)moveMeteor:(UIImageView *)t;

Any ideas of what the problem is and a solution to it?

  • Are you sure changing the center attribute moves the image? Can you try just updating the y offset? Also, have you confirmed the moveMeteor method gets called? – JeffRegan Jul 22 '13 at 21:31
  • yes it does. the self moveMeteor:six, works. However, the NSTimer doesnt – user2607954 Jul 22 '13 at 22:04

2 Answers2

0

Your method is actually not getting the right parameter, you get NSTimer as a parameter. so instead

-(void)moveMeteor:(UIImageView *)t

the method should be like this

-(void)moveMeteor:(NSTimer *)timer

and you can't treat NSTimer as an UIImageView. :)

*Edit

I would suggest you to do something like

-(void)viewDidLoad{
    UIImageView *six = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"Meteor.png"]];
    CGRect rectSix = CGRectMake(arc4random() % (250), arc4random() % (250), 35, 35);
    [six setFrame:rectSix];
    [self.view addSubview:six];
    [self moveMeteor:six];
    [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(moveMeteor:) userInfo:six repeats:YES];    
}

and update your method to this

- (void)moveMeteor:(NSTimer *)timer {
    UIImageView *six = timer.userInfo;
    six.center=CGPointMake(six.center.x, six.center.y + 1); 
}

just for information, I am passing the UIImageView in the dictionary as userInfo.

Alex Markman
  • 1,450
  • 1
  • 13
  • 15
  • But I need to treat it as a UIImageView. If I used a perform selector method would that change anything? If so, I tried implementing and it works once and then stops. – user2607954 Jul 22 '13 at 21:55
  • It is not recognizing userInfo because it is undeclared in the moveMeteor function – user2607954 Jul 22 '13 at 22:17
  • @user2607954 have you checked if it works? I have tested and worked well. – Alex Markman Jul 22 '13 at 22:44
  • Yes it works now. Just wondering, if I were to code in multiple image views and call this same function similarly, would it still work and run relatively smoothly? – user2607954 Jul 23 '13 at 17:40
  • @user2607954 you can store a NSMutableArray as an userInfo, but make sure you make it global and just keep on adding UIImageView, but in moveMeteor method you have to use a loop to get all UIImageViews. – Alex Markman Jul 23 '13 at 20:50
0

Or you could do something like this, which takes advantage of UIView's animateWithDuration and should look much better. No timer required.

// in your interface declaration...
@property (nonatomic, assign) BOOL meteorShouldAnimate;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

// in your implementation...
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.meteorShouldAnimate = YES;
    [self moveMeteorWithAnimationOptions:UIViewAnimationOptionCurveEaseIn]; // ease in to the animation initially
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    self.meteorShouldAnimate = NO;
}


- (void)moveMeteorWithAnimationOptions: (UIViewAnimationOptions) options {
    __weak MyViewController * weakSelf = self;
    if (self.meteorShouldAnimate) {
        [UIView animateWithDuration:0.2 delay:0.0 options:options animations:^{
            self.imageView.transform = CGAffineTransformMakeTranslation(20.0, 20.0); // or whatever x and y values you choose
        } completion:^(BOOL finished) {
            MyViewController * strongSelf = weakSelf; // in case our view controller is destroyed before our completion handler is called
            if (strongSelf) {
                UIViewAnimationOptions nextOptions = strongSelf.meteorShouldAnimate ? UIViewAnimationOptionCurveLinear : UIViewAnimationOptionCurveEaseOut; // linear if we're continuing to animate, ease out if we're stopping
                [strongSelf moveMeteorWithAnimationOptions:nextOptions];
            }
        }];
    }
}
Nicholas Hart
  • 1,734
  • 13
  • 22
  • Ouch, sorry. Well, one thing I would recommend regardless is to start your animation from viewDidAppear, not viewDidLoad. I'd stop it in viewWillDisappear. – Nicholas Hart Jul 22 '13 at 22:55