views:

156

answers:

4

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

+1  A: 

Try animating the uiview's frame property.

tadej5553
Do you mean the UIButton's frame property? I tried that but the same problem occurs - you can only press the button when it reaches its final destination...
Smikey
Well, than there's only one solution: you'll have to handle the touch events yourself. When the user touches the screen, you'll have to find out if the button is currently at the touch point, and so on...
tadej5553
Ouch... that won't be pretty, given I have about 10 buttons active at the same time. I've just found a similar post though - http://stackoverflow.com/questions/2412787/how-can-i-detect-a-touch-on-an-animated-subview-or-click-an-animated-uibutton and it seems physically moving the button one increment at a time using an NSTimer will have to be the way to do it. I'm not sure quite how I'll manage with several buttons simultaneously, and being contantly regenerated... :(
Smikey
Even if you do that, your button will still be unavailable to touch when it is moving. In the best case that would be 50% of the total move time.No matter how small do you make your position increments.
tadej5553
+1  A: 

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.

Tim Rupe
Ok cool, that's kind of what I expected. Since I'll have several of these buttons active at a time, I guess I'll have to test the bounds of the presentationLayer for all active buttons. Hope that won't be too performance hogging, but will give it a go. Thanks for the suggestions!
Smikey
+1  A: 

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.

Engin Kurutepe
Yeah, I'm actually trying to simulate a lot of floating bubbles which the player can pop. I'll give this approach a go - thanks!
Smikey
A: 

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];
        }
    }
}
Smikey