I've looked high and low for a solutions for this and I've gotten nowhere.
I want to develop an iOS app that is pretty much a simple tap, animate and play sound.
So I'd have a map of say a farm and when I click the sheep, it will animate and make some noise. I have managed to achieve this and its all good.
My issue and question is being able to click an already animating button.
So when the view loads i would like to have cloud buttons move from left to right in the sky slowly, again I have already achieved this using this code
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidLoad];
[UIView animateWithDuration:8.5
delay:1.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut
animations:^{
cloudAnimate.center = CGPointMake(-100.0, 100.0);
}
completion:NULL];
}
BUT, when the buttons are animating from left to right and back they are not clickable so the touch I want for spinning the clouds and playing the sound doesn't work. I have read a few other posts that say that this can not be done but I have a load of apps on my iPhone and iPad that do this so anyone have any idea how this can be achieved?
Any help would be greatly appreciated as I'm pulling my hair out.
Thanks in advance.
EDIT:
OK so thanks to a combination of answers below I have almost got this to work
So I am using this to set the initial CGPoint:
- (void)viewDidLoad
{
[super viewDidLoad];
// Move UIView
cloudAnimate.center = CGPointMake(400.0, 100.0);
}
And to animate the cloud from left to right I am using the same code as i originally had with a slight tweak:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidLoad];
[UIView animateWithDuration:8.5
delay:1.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
cloudAnimate.center = CGPointMake(100.0, 100.0);
}
completion:NULL];
}
And now instead of IBAction I am using this:
-(void) touchesBegan:(NSSet*) touches withEvent:(UIEvent *) event {
CGPoint point = [[touches anyObject] locationInView:self.view];
if([self.cloudAnimate.layer.presentationLayer hitTest:point]) {
SystemSoundID soundID;
NSString *soundFile = [[NSBundle mainBundle] pathForResource:@"Super_Mario_Bros_Mushroom" ofType:@"mp3"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef) [NSURL fileURLWithPath:soundFile], & soundID);
AudioServicesPlaySystemSound(soundID);
cloudAnimate.imageView.animationImages = [NSArray arrayWithObjects:[UIImage imageNamed:@"panda-png-chewing"],[UIImage imageNamed:@"panda-png-happy"],nil];
cloudAnimate.imageView.animationDuration = 0.2;
cloudAnimate.imageView.animationRepeatCount = 6;
[cloudAnimate.imageView startAnimating];
}
}
So now it does almost exactly what I want but if you hit the cloud when it is at its finishing CGPoint (even though it will carry on moving) the app crashes. If I hit it while it is moving it makes a sound and animate.
Anyones have any suggestions as to why this is?
The short answer is that you can't tap views while they are animating. The reason is that the views don't actually travel from the start to the end location. Instead, the system uses a "presentation layer" in Core Animation to create the appearance of your view object moving/rotating/scaling/whatever.
What you have to do is attach a tap gesture recognizer to a containing view that completely encloses the animation (maybe the entire screen) and then write code that looks at the coordinates of the tap, does coordinate conversion, and decides if the tap is on a view that you care about. If the tap is on a button and you want the button to highlight you'll need to handle that logic too.
I have a sample project on github that shows how to do this when you are doing both UIView animation and Core Animation with CABasicAnimation (which animates layers, not views.)
Core Animation demo with hit testing
You may want to use
UIViewAnimationOptionAllowUserInteraction
as well.When you animate a view you set the view in its final state. This way you can only detect touches in the final position where you are animating.
However, it is possible to go around that issue. You need to catch the touch event and comparate with the presentationLayer.
The presentation layer has information about the visual position of the CALayer that is associated with your cloudAnimate.