CATransiton Flicker w/ background animations - iphone

I am using a CATransition to shuffle CALayer's in and out of a UIView (the UIView isn't fullscreen.) The new layer enters from the right and the old layer leaves to the left. The CAlayer's have their contents properties set to CGImageRefs. Here is what I believe to be the pertinent code:
CATransition *transition = [CATransition animation];
transition.duration = transitionTime;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
transition.delegate = self;
[view.layer addAnimation:transition forKey:nil];
// onDeck is the layer stage-right and is about to enter
[view.layer addSublayer:onDeck];
// onStage is the layer currently in the middle of the view and is exiting
if (onStage)
[onStage removeFromSuperlayer];
This animation occurs, there is a 1 second pause, and then a new image is animated in etc, etc. This works beautifully in the simulator but on my 2nd generation iPod Touch, there are occasional hiccups in the animation. Specifically, the layer being animated in will flicker on top of the currently displayed layer. These flickers are not consistent but are noticeable.
What I've narrowed down to be the likely culprit(s) are the background animations I have going on behind this transition. There are a few dozen CAlayer's flying in and out of the full-screen view in the background. When I remove these background animations or set the animation duration for the transition to a much higher duration (2 seconds +) the animation performs fine (I'd like the animation time to be 0.75 seconds.)
My first thought (which seems to be backed up by the above observations) is that I am pushing CoreAnimation (at least for my iPod) too hard and need to compromise. Why I am having trouble accepting this is that the scene renders very nicely (little to no lag) aside from this occasional flicker.
If anyone has any input on this issue, or CoreAnimation optimization in general, I'd be very much appreciative!
Thanks for reading
Update:
Had a chance to test this on an iPhone 4 and the flickering never occurred. Additionally, CoreAnimation instrument confirmed that, on my 2nd generation iPod Touch, I am consistently getting high 40s to low/mid 50s in FPS.

Just a guess: have you tried managing your CATransactions at all?
You might try bracketing your modifications to the scene like this:
[ CATransaction begin ]
... your animations ...
[ CATransaction commit ] ;

I would get rid of all shadows (if you have any) and then check if performance improves. I've seen that shadows, particularly shadows on CAShapeLayers, have a great impact on the smoothness of animations.

Related

CABasicAnimation lag issues with IOS 5 and RemoteIO

I am creating a musical instrument for the iPhone. In my app when i touch a CALayer a note plays and the layer does a wiggle for the duration of the note.
This was all working perfectly on iOS 4.1, however I just upgraded to iOS 5.0.1 and experienced major lag issues if I pressed multiple notes in succession. After much pain I have narrowed it down to the following wiggle animation code for the CALayer that is touched.
// here is an example wiggle
CABasicAnimation *wiggle = [CABasicAnimation animationWithKeyPath:#"transform"];
wiggle.duration = 0.1;
//wiggle.repeatCount = 1e100f;
wiggle.repeatCount = 100;
wiggle.autoreverses = YES;
wiggle.toValue = [NSValue valueWithCATransform3D:CATransform3DRotate(fret.fretLayer.transform,0.2, 0.0 ,1.0 ,2.0)]; //angle, x , y , z
wiggle.fromValue = [NSValue valueWithCATransform3D:CATransform3DRotate(fret.fretLayer.transform,-0.2, 0.0 ,1.0 ,2.0)]; //angle, x , y , z
// doing the wiggle
[note.noteLayer addAnimation:wiggle forKey:#"wiggle"];
If I block out the last line where the animation is added to the layer all lag disappears immediately. It feels like the main thread is being blocked somehow, or its not running on the main thread, but I have tried invoking the function with performSelectorOnMainThread:withObject:waitUntilDone:NO and it made no difference. If I press loads of notes really quickly the whole app pauses, and then a second or so later its like it catches up with itself and suddenly all the sound plays and the animation finally begins all choppy and choked after that.
Does anyone know of any unusual issues with CABasicAnimation in iOS 5? Is there any alternative animation APIs that I could try that could also animate CALayers for an unspecified duration on repeat? Anyone have any suggestions or guesses as to how to fix/what the problem could stem from?
EDIT:
I have determined it is definitely nothing to do with nature of the animation. I replaced the wiggle code with code that simply fades the colour like so, but I am still getting the same lag effect with notes being laggy to play.
CABasicAnimation *wiggle = [CABasicAnimation animationWithKeyPath:#"backgroundColor"];
wiggle.duration = 2;
wiggle.toValue = (id)[UIColor whiteColor].CGColor ;
wiggle.fromValue = (id)[UIColor colorWithRed:0.1 green:0.2 blue:0.8 alpha:0.4].CGColor;
Suspect it could be something to do with a change in CoreAudio/RemoteIO perhaps? And that is interfering with the main thread or visa versa?
Finally worked out the true nature of what was causing the issue. Turns out there is a problem with using shouldRasterize = YES in iOS 5.
In my app I am embedding 100+ CALayers in a UIView which has had its layer set to shouldRasterize = YES. I created a new app from the ground up and added bits in until i replicated the problem. Ultimately it turned out to be this. In iOS 4 this isnt a problem. In iOS 5 it is. If I turn shouldRasterize off the problem disappears immediately.
Have submitted a bug report with apple.
Just for test, try to replace CABasicAnimation with [UIView beginAnimation/commitAnimation].

Hidden CATransition animations on iOS 4 (camera iris animation)

I'm trying to achieve the camera iris animation from the Apple camera app. It is also found in numerous other apps like RedLaser, Sudoku Grab, so it seems to be fine with Apples rules even thou it's private.
The hidden CATransition animations are dokumented here for example:
http://iphonedevwiki.net/index.php/UIViewAnimationState
However I'm not able to get any of the hidden ones to work, public ones work fine thou. Could this be a change of iOS 4? All information I do find on those hidden animations seems a little dated.
Here is my code:
CATransition *animation = [CATransition animation];
animation.delegate = self;
animation.duration = 2.0;
animation.timingFunction = UIViewAnimationCurveEaseInOut;
animation.type = #"cameraIris";
[self.window.layer addAnimation:animation forKey:nil];
Using "reveal" as 'animation.type' works fine with my code.
Has anybody played around with those on iOS 4 yet? Or is the issue totally different?
RedLaser uses this effect on iOS 4 (it's not an iOS 4 optimised app thou).
The animation is found in these other apps because it is Apple's implementation. Accessing the camera API will cause the shutter opening animation to appear. If you want to use this animation for your app in a non-camera api related way, then you will have to roll your own animation.

Iphone 3d Animation

I recently posted my problem but neither did I get an answer here or elsewhere, nor can I find my post again...
Anyway, I want to try to state my point clearer, hoping you can help me this time maybe.
The part of code, this is about, looks like this:
CABasicAnimation *animation = nil;
CATransform3D transform;
animation = [CABasicAnimation animationWithKeyPath:#"transform"];
transform = CATransform3DMakeRotation(1.57f, 0.0f, 1.0f, 0.0f);
value = [NSValue valueWithCATransform3D:transform];
[animation setToValue:value];
[animation setAutoreverses:YES];
[animation setDuration:0.5f];
[[shieldView layer] addAnimation:animation forKey:#"180"];
What happens, is that I have a UIImageView with an image in it. I take this View and rotate it around the z-Axis for half a turn (1.57 measured in arc).
Now there is another View overlapping with the one being transformed in 3d. This leads to my problem: As soon as the transformation is executed, the transformed layer is sort of clipped against the overlapping view, but wrong, so that u see a 'flickering' effect to both Views.
Now I don't know, why this happens. I would expect the view to be animated appropriately, without this 'bug', and the Views to be just displayed correctly.... since I do not do anything special.
I have tried to overlay 2D transformations, using the 'beginAnimations' and 'commitAnimations' context, which works fine. This means, the problem is sort of hidden in the 3D rotation of my View.
You guys have any clues on this? I hope I explained that well enough.
If these are two sibling views (subviews of the same superview), their layers can overlap, potentially creating the visual artifacts you see. You should be able to either reposition the views relative to one another in the view hierarchy (make one view a level above or below the other) or directly set the Z position of the view's layer. If you do something like
shieldView.layer.zPosition = 1.0f;
your shieldView's layer should be slightly above the other view's layer (unless I'm remembering the coordinate space wrong and it needs to be -1.0f) and you should be able to avoid the clipping you see now.

How can I replicate the trashing animation of Mail.app

In my iPhone app, I have put a UIBarBUtton of type UIBarButtonSystemItemTrash in my UIToolBar. When pressed, I'd like to replicate the animation of Mail.app: the bin opens, the UIView folds and flies into it.
Is there a way to access this animation ithrough the iPhone SDK?
Presently I am using a custom made animation, but there are some limits; for example, I cannot animate the bin itself.
Do you have any suggestion? Code samples?
Cheers,
Davide
Use the suckEffect type on an animation. Also: spewEffect, genieEffect, unGenieEffect, twist, tubey, swirl, cameraIris, cameraIrisHollowClose, cameraIrisHollowOpen, rippleEffect, charminUltra, zoomyIn, and zoomyOut. Doesn't work in the simulator.
CATransition *animation = [CATransition animation];
animation.type = #"suckEffect";
animation.duration = 2.0f;
animation.timingFunction = UIViewAnimationCurveEaseInOut;
view.opacity = 1.0f;
[view.layer addAnimation:animation forKey:#"transitionViewAnimation"];
Note: Code snippet was pulled from a larger codebase. I hope it works :)
Just to add some info:
You can use "suckEffect" with the standard +[UIView setAnimationTransition:forView:cache:]. Just pass the number 103 to the animationTransition variable. This won't avoid your app being rejected by Apple though :p
"spewEffect", "genieEffect", "unGenieEffect", etc. no longer exist on iPhoneOS 3.x. The only undocumented transition left are "cube" (--), "rippleEffect" (110), the three "cameraIris" effects (105,106,107) and "suckEffect" (103).
See http://www.iphonedevwiki.net/index.php?title=UIViewAnimationState for detail.
Also, to animate the bin (with private API): http://www.iphonedevwiki.net/index.php?title=UIToolbar#Animating_the_trash_can_icon.
Unfortunately, I think this is going to need to be an entirely custom animation. The UIView folding can be approximated using Core Animation, perhaps by adding perspective to the CATransform3D of the UIView's underlying layer to distort the UIView into a trapezoid which gets sucked into the trash can.
As far as the trash can, you can create a UIBarButtonItem using initWithCustomView:, which might let you insert a custom UIView that has an animatable trashcan. It looks like the trash can has two elements, the can base and the lid, which are rotated independently to open and close the can. Draw PNGs for both, make UIImageViews for them, and make them subviews of the UIBarButtonItem custom view. For opening and closing, apply rotational transforms to them to animate the subviews.
I'm not sure if this is an answer but here is lib that do "genie effect" so it's quite similar to what you want achieve.
CGRect endRect = CGRectMake(30, 40, 50, 60);
[view genieInTransitionWithDuration:0.7
destinationRect:endRect
destinationEdge:BCRectEdgeTop
completion:^{
NSLog(#"I'm done!");
}];

CATransition showing a single frame from the end of the transition before the animation starts

I am using some code which was originally taken from the Apple sample ViewTransitions to swap two views with each other.
CATransition *animation = [CATransition animation];
[animation setDelegate:self];
[animation setType:kCATransitionFade];
[animation setDuration:0.3f];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[[container layer] addAnimation:animation forKey:#"swap"];
When my transition executes on the devlice, I sometimes get a single frame flash of the final frame of the transition animation and then the animation plays smoothly. This gives a very jarring effect of the 2nd view which flickers in and then out again before the smooth animation executes.
The main difference between my example and the Apple example is that my views are not full screen, I have a container UIView which contains both sub-views and I am applying my animation to the container layer instead the root view layer. I can not see that this should make much difference though.
This issue does not happen at all in the simulator and is intermittent (about 60-70% of the time) on the device. Is anyone else seeing this behaviour and if so, how did you work around it?
Updated with more information:
The code was originally part of a multi part animation but I moved it to be triggered by a button to isolate the problem. The issue occurs in both situations.
The animation delegates were implemented but have been removed to try and isolate the problem.
The views are not changing during the animation and in fact [container setUserInteractionEnabled:NO] is being called (but the problem also happens without user interaction being disabled).
I have tried starting the animation from the middle of the transition and ending before the end using setStartProgress and setEndProgress, again the problem persists.
I have tried slowing the animation duration right down as well with no effect.
To begin to answer your question more information is needed - for instance, how are you calling that code (e.g. is it bound to a button as an action or is it triggered automatically somehow), have you implemented the delegate methods to start and stop the animation (and what is contained within them), and are your views changing at some point during the animation? Timing between the simulator and device will be off - you should not rely on the simulator to test animation as everything will happen quicker.
Solved, I had missed a key piece of this that I thought was irrelevant ;) After setting up the animation in the code sample above, I was swapping the views out in the same method.
Moving it to the animationDidStart delegate fixed the issue.