0

So I just implemented an adBannerView into my app (a basic juggling game) that, when expanded (ie calls bannerViewActionShouldBegin on delegate), causes the CAKeyFrameAnimations on the CALayers (over defined bezier curves) to continually stop and call animationDidStop on their delegate. By continually, I mean once about every .001 seconds.

I have already implemented a pause method into my game, which, when called (via action tied to UIButton), sets the CALayer speed of each ball object to 0.0, effectively stopping them in the midst of their animation. There is a boolean condition that defines whether or not the game is paused, which determines what portion of the pause code is executed when the method is called. Here is the code:

- (IBAction)pauseButton:(id)sender {
    if (isPaused == NO) {
        isPaused = YES;
        [_pauseButtonOutlet setImage:playButtonImage forState:UIControlStateNormal];
        [_pauseButtonOutlet setImage:playButtonImage forState:UIControlStateSelected];
        for (x = 0; x < [ballsArray count]; x++) {
            [[ballsArray objectAtIndex:x] setTimeOffset:[[ballsArray objectAtIndex:x] convertTime:CACurrentMediaTime() fromLayer:nil]];
            [[ballsArray objectAtIndex:x] setBeginTime:CACurrentMediaTime()];
            [[ballsArray objectAtIndex:x] setSpeed:0.0];
        }
        [_statusLabel setText:@"Paused"];

    } else if (isPaused == YES) {
        isPaused = NO;
        [_pauseButtonOutlet setImage:pauseButtonImage forState:UIControlStateNormal];
        [_pauseButtonOutlet setImage:pauseButtonImage forState:UIControlStateSelected];
        for (x = 0; x < [ballsArray count]; x++) {
            [[ballsArray objectAtIndex:x] setTimeOffset:[[ballsArray objectAtIndex:x] convertTime:CACurrentMediaTime() fromLayer:nil]];
            [[ballsArray objectAtIndex:x] setBeginTime:CACurrentMediaTime()];
            [[ballsArray objectAtIndex:x] setSpeed:1.0];
        }
        [_statusLabel setText:@""];

    }

}

This method also serves (or so I thought) as a convenient way to suspend application activity while the adBannerView's contents are expanded. However, what I find is that when I return to the interface, my CALayer balls will be at the end of their animations (the point where the character catches them), but the game will still be paused. Unpausing causes them to continue as if the animations are starting anew (when a ball lands, the animation stops, and a new one is created in the opposite direction).

I figured that perhaps something was happening to the presentation layer during the displaying of the ad (since, at the beginning of each animation, I move the CALayer itself to the end point but still animate the presentation layer across the bezier curve from the starting point). But after adding a quick NSLog statement to my animationDidStop methods (I say "methods" because there are four ball classes in total, each designed to animate over a defined path, and each having its own animationDidStop delegate method), I found that, for some odd reason, the stopping methods for all instantiated balls are called continuously and in very close intervals (~.001s). This activity ceases when the ad is closed (and I use bannerViewActionDidFinish to again call the pause function, automatically resuming the game).

I would love for someone to help me out with this. I have formulated a pseudo-solution (obtain ball positions when paused from banner, create nsnumber arrays for x and y values, and upon unpausing restart animations from point where they left off with bezier curve whose control points and main points are calculated dynamically), but this makes the animation irregular and non-conforming to the standard ball animation. I would really appreciate being able to just resume the animations from where they left off, as in the regular pause method.

Thanks for reading all of this, and any help is greatly appreciated. Also, if you need/want to see any of the rest of my code, I'd be happy to oblige.

Update: After doing a bit of code consideration, my best guess is that the banner display is forcing the animation to stop prematurely, which causes it (the ball CALayer) to call the animationDidStop method on its delegate. Since this method automatically creates a new animation, an endless cycle of new-stop-new-stop-new-etc is created. This would also explain why sometimes the ball is on the side opposite of that on which it was supposed to land, while sometimes it lands on the expected side.

From this, I gather that the adBannerView, when expanded, forces all CAAnimation to cease. If I am correct about this, is there any way around it?

ghostsax
  • 1
  • 4

1 Answers1

0

Maybe when you resume layer, you can check whether there is added animation. If no, you can add new animation with correct timeOffset.

Also, is there any reason you don't use fast enumeration compared to using for (x = 0; x < [ballsArray count]; x++) ?

  • I was thinking about making it do something like that. My question: how does one create a bezier curve animation in which the animated layer is already part way through the animation (assuming that's what you mean by "time offset"). And as for how I enumerate, well, I dunno. I worked with BASIC and C a lot before I got into objective C and I just feel more comfortable doing it the old fashioned way. Lol. – ghostsax Jul 19 '14 at 22:37
  • You can just add the same bezier curve animation. Then set the animation timeOffset to the paused timeOffset. This is how you enumerate in obj c, for (CALayer *ball in ballsArray) { [ball setTimeOffset:[ball convertTime:CACurrentMediaTime() fromLayer:nil]]; } – Wan Lutfi Wan Hatta Jul 19 '14 at 22:58
  • Okay, so before the ad banner is opened (bannerViewActionShouldBegin), I add NSNumber values to "offsetsArray" that contain a number of type double that is returned when calling the method "timeOffset" on the animated CALayers. When I close the bannerView (bannerViewActionDidFinish), I create new animations for each layer with their original bezier curve paths, and do the following: set the begin time of each animation to CACurrentMediaTime(), set the speed to 1.0, and set the timeOffset to the number recorded earlier. However, it still does not work. Do you any specific method to do this? – ghostsax Jul 23 '14 at 22:10
  • When banner is finished and you checked no animation to that layer, first you should set layer speed = 1, timeOffset = 0, beginTime = 0. Then you just need to set timeOffset to your animation. The animation should start at the given timeOffset. I have used this technique before. Do try if it works. CAKeyFrameAnimations *positionAnim = ...; positionAnim.timeOffset = timeOffset; – Wan Lutfi Wan Hatta Jul 25 '14 at 00:07
  • TimeOffset for positionAnim means that the exact duration from start layer animation until you begin your banner, for example maybe its 3 seconds. – Wan Lutfi Wan Hatta Jul 25 '14 at 00:23