7

So I've got a problem with buttons and animations. Basically, I'm animating a view using the UIView animations while also trying to listen for taps on the button inside the view. The view is just as large as the button, and the view is actually a subclass of UIImageView with an image below the button. The view is a subview of a container view placed in Interface Builder with user interaction enabled and clipping enabled. All the animation and button handling is done in this UIImageView subclass, while the startFloating message is sent from a separate class as needed.

If I do no animation, the buttonTapped: message gets sent correctly, but during the animation it does not get sent. I've also tried implementing the touchesEnded method, and the same behavior occurs.

UIImageView subclass init (I have the button filled with a color so I can see the frame gets set properly, which it does):

- (id)initWithImage:(UIImage *)image {
    self = [super initWithImage:image];
    if (self != nil) {
        // ...stuffs

        UIButton *tapBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        tapBtn.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
        [tapBtn addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
        tapBtn.backgroundColor = [UIColor cyanColor];
        [self addSubview:tapBtn];

        self.userInteractionEnabled = YES;
    }
    return self;
}

Animation method that starts the animation (if I don't call this the button works correctly):

- (void)startFloating {
    [UIView beginAnimations:@"floating" context:nil];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    [UIView setAnimationDuration:10.0f];
    self.frame = CGRectMake(self.frame.origin.x, -self.frame.size.height, self.frame.size.width, self.frame.size.height);
    [UIView commitAnimations];
}

So, to be clear:

  • Using the UIView animation effectively disables the button.
  • Disabling the animation causes the button to work.
  • The button is correctly sized and positioned on screen, and moves along with the view correctly.
bensnider
  • 3,742
  • 1
  • 24
  • 25

3 Answers3

21

This resolves the issue:

[UIView animateWithDuration:20 delay: 0.0 options: UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
  ...
user726522
  • 276
  • 2
  • 4
8

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.

Mr. Berna
  • 10,525
  • 1
  • 39
  • 42
  • Hmm after doing some testing I do believe you are correct. This seems fairly janky though, against the expectations for animation. When you say "make the animation yourself" you are referring to an NSTimer style animation? Also I assume implementing the touches family of methods would exhibit the same behavior? – bensnider Jun 03 '10 at 20:22
  • So yes using an NSTimer animation I am able to use the button in this manner. Thanks. – bensnider Jun 04 '10 at 14:03
  • You could also do touch tracking yourself and not rely on the button at all. It would still require some manual animation, but may give you better results for interaction. – axiixc Jun 14 '11 at 19:01
5

... was experiencing this same problem because my code was doing one large animation per block. I made an NSTimer based solution, like the one suggested above, and it worked... yet the movement was jerky (unless I inserted animation within every timer event trigger).

So, since animation was required anyway, I found a solution which requires no timer. It animates only a short distance and thus the button click is still accurate, with only a small error which is my case is very unnoticeable in the UI, and can be reduced depending on your params.

Note below that the error at any given time is < 15.0, which can be reduced for more accuracy depending on your animation speed requirements. You can also reduce the duration time for more speed.

- (void)conveyComplete:(UIView*)v
{
    [self convey:v delay:0];
}

- (void)convey:(UIView*)v delay:(int)nDelay
{
    [UIView animateWithDuration:.5 
                          delay:nDelay
                        options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction)  
                     animations: ^
                    {
                        CGRect rPos = v.frame; 
                        rPos.origin.x -= 15.0;
                        v.frame = rPos;
                    }
                    completion: ^(BOOL finished)
                    {
                        [self conveyComplete:v];
                    }];

}
paiego
  • 3,619
  • 34
  • 43