Issue with CAAnimation and CALayer Transforms - iphone

I have a CALayer that I want to animate across the screen. I have created two methods: one slide open the layer and one to slide close. These both work by assigning a property to the layer's transform property.
Now I want to use a CAKeyFrameAnimation to slide open the layer. I got this working so the layer slides open, but now I can't slide the layer close using my old method. I am trying to figure out why this is. Any help would be great.
Code for my CALayer:
- (id)init
{
if( self = [super init] )
{
bIsOpen = NO;
closeTransform = self.transform;
openTransform = CATransform3DMakeTranslation(-235.0, 0.0, 0.0);
}
return self;
}
- (void)closeMenu
{
if( bIsOpen )
{
self.transform = closeTransform;
bIsOpen = !bIsOpen;
}
}
- (void)openMenu
{
if( !bIsOpen )
{
CAKeyframeAnimation *closeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
closeAnimation.duration = 1.0;
closeAnimation.removedOnCompletion = NO;
closeAnimation.fillMode = kCAFillModeForwards;
closeAnimation.values = [NSArray arrayWithObjects:[NSValue valueWithCATransform3D:closeTransform],[NSValue valueWithCATransform3D:openTransform],nil];
closeAnimation.timingFunctions = [NSArray arrayWithObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[self addAnimation:closeAnimation forKey:#"transform"];
bIsOpen = !bIsOpen;
}
}

I figured this out. My problem was that the animation animates the object as if it modifying the property that is changed ( in this case the transform ), but the value isn't actually changed. I believe this has to do with the Presentation Layer vs. the Model Layer. So when the animation was done for opening the layer, the layer was positioned on the screen correctly, but its transform has not actually changed. So to fix this I used the (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag delegate method to set the transform to the opened state once the animation was complete.

Related

Preventing a rotated CGAffineTransform from being wrecked by view autorotation

I have a subclass of a UIImageView containing some gesture recognisers that I'm using to apply transforms to itself. I'm having no issues with panning or scaling, but the rotated transform is causing problems when the device itself is rotated. Basically each time the device is rotated it will have the effect of scaling the image...
I guess it makes sense that the rotation of everything might cause problems with a rotated transform but does anyone know any ways around this kind of behaviour? Preferably something that can be implemented within the UIImageView subclass? I need other sibling views to autoresize so I can't disable "autoresize subviews" in the parent view.
Here's the code responsible for creating the rotated transform if it helps:
- (void)setUpRotation
{
UIRotationGestureRecognizer *newRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(handleRotationGesture)];
newRecognizer.delegate = self;
self.rotationRecognizer = newRecognizer;
[self addGestureRecognizer:self.rotationRecognizer];
[newRecognizer release];
self.userInteractionEnabled = YES;
}
- (void)handleRotationGesture
{
// Initial state
if(self.rotationRecognizer.state == UIGestureRecognizerStateEnded)
{
lastRotation = 0.0;
return;
}
CGFloat rotation = 0.0 - (lastRotation - self.rotationRecognizer.rotation);
CGAffineTransform currentTransform = self.transform;
self.transform = CGAffineTransformRotate(currentTransform,rotation);
lastRotation = self.rotationRecognizer.rotation;
}
This is on iOS 5.0 btw.

iOS; How to scale UIimageView (permanently) and then move it

I've hit a wall here. I know how to move an Image using "CGAffineTransformMakeTranslation" and I also know how to scale an image using"CGAffineTransformMakeScale" but for the life of me, I can't seem to get one Image to do both of these and stay that way. It scales to the desired size for about a split second and then immediately reverts to its original size and moves to the desired location. What I need is for the image to get big, STAY big, and then move to a new location (while permanently staying its new size).
Here is what I've got going on in my .m file:
-(IBAction)PushZoomButton {
[UIWindow animateWithDuration:1.5
animations:^{
JustinFrame.transform = CGAffineTransformMakeScale(2.0, 2.0);
JustinFrame.transform = CGAffineTransformMakeTranslation(10.0, 10.0);}];
[UIWindow commitAnimations];}
Any help with this would be appreciated!
you can use CGAffineTransformConcat, for instance:
JustinFrame.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(2.0, 2.0), CGAffineTransformMakeTranslation(10.0, 10.0));
You may need to adapt the translation to (5, 5) since you have doubled the scale
The second transform you set overrides the first one. You need to concat both transform actions into one, as Luis said. Another way of writing that would be:
CGAffineTransform transform = CGAffineTransformMakeScale(2.0, 2.0);
transform = CGAffineTransformTranslate(transform, 10, 10);
JustinFrame.transform = transform;
You may need to look into CoreAnimation, basically what UIView animation is controlling under the hood. If you set up a CAAnimation, then what you want to achieve is done with the fillMode property of the animation.
Here's some example code to make a UIView look like it's opening like a door (copy pasted some code I have, but perhaps you could modify it and find it useful):
- (void) pageOpenView:(UIView *)viewToOpen duration:(NSTimeInterval)duration pageTurnDirection:(PageTurnDirection) p{
// Remove existing animations before stating new animation
[viewToOpen.layer removeAllAnimations];
// Make sure view is visible
viewToOpen.hidden = NO;
// disable the view so it’s not doing anythign while animating
viewToOpen.userInteractionEnabled = NO;
float dir = p == 0 ? -1.0f : 1.0f; // for direction calculations
// create an animation to hold the page turning
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
transformAnimation.removedOnCompletion = NO;
transformAnimation.duration = duration;
transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
CATransform3D startTransform = CATransform3DIdentity;
if (p == NEXT_PAGE) {
// orig values
startTransform.m34 = 0.001f;
}else {
// orig values
startTransform.m34 = -0.001f;
}
// start the animation from the current state
transformAnimation.fromValue = [NSValue valueWithCATransform3D:startTransform];
// this is the basic rotation by 90 degree along the y-axis
CATransform3D endTransform = CATransform3DMakeRotation(3.141f/2.0f,
0.0f,
dir,
0.0f);
// these values control the 3D projection outlook
if (p == NEXT_PAGE) {
endTransform.m34 = 0.001f;
endTransform.m14 = -0.0015f;
}else {
endTransform.m34 = -0.001f;
endTransform.m14 = 0.0015f;
}
transformAnimation.toValue = [NSValue valueWithCATransform3D:endTransform];
// Create an animation group to hold the rotation
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
// Set self as the delegate to receive notification when the animation finishes
theGroup.delegate = self;
theGroup.duration = duration;
// CAAnimation-objects support arbitrary Key-Value pairs, we add the UIView tag
// to identify the animation later when it finishes
[theGroup setValue:[NSNumber numberWithInt:[(BODBookPageView *)viewToOpen pageNum]] forKey:#"animateViewPageNum"]; //STEPHEN: We set the tag to the page number
[theGroup setValue:[NSNumber numberWithInt: p] forKey:#"PageTurnDirection"];
[theGroup setValue:[NSNumber numberWithBool:YES] forKey:#"isAnimationMidpoint"]; // i.e. is this the first half of page-turning or not?
// Here you could add other animations to the array
theGroup.animations = [NSArray arrayWithObjects:transformAnimation, nil];
theGroup.removedOnCompletion = NO; // THIS LINE AND THE LINE BELOW WERE CRUCIAL TO GET RID OF A VERY HARD TO FIND/FIX BUG.
theGroup.fillMode = kCAFillModeForwards; // THIS MEANS THE ANIMATION LAYER WILL STAY IN THE STATE THE ANIMATION ENDED IN, THEREBY PREVENTING THAT ONE FRAME FLICKER BUG.
// Add the animation group to the layer
[viewToOpen.layer addAnimation:theGroup forKey:#"flipViewOpen"];
}

How do I animate CATransform3Ds with a CAKeyframeAnimation?

I've used CAKeyframeAnimations to animate a layer's transform.rotation and transform.translation.x properties, but I'm having trouble animating the transform property implicitly. I have a layer that must animate between two states and CABasicAnimation's default interpolation is totally incorrect and doesn't follow the path I want. CAKeyframeAnimation to the rescue, or so I thought. Any attempt to animate transform using a CAKeyframeAnimation results in the view immediately snapping to the final transform while the other animations run. If I remove the first half of the following function and let my "transform" events use the CABasicAnimation on the bottom, it animates just fine - albeit with incorrectly interpolated transforms along the way.
My layer delegate has implemented the following:
- (id <CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
if ([event isEqualToString:#"transform"])
{
CGSize startSize = ((CALayer *)self.layer.presentationLayer).bounds.size;
CGSize endSize = self.layer.bounds.size;
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:event];
animation.duration = 0.25;
NSMutableArray *values = [NSMutableArray array];
int stepCount = 10;
for (int i = 0; i < stepCount; i++)
{
CGFloat p = i / (float)(stepCount - 1);
CGSize size = [self interpolateBetweenSize:startSize andSize:endSize percentage:p];
CATransform3D transform = [self transformForSize:size];
[values addObject:[NSValue valueWithCATransform3D:transform]];
}
animation.values = values;
return animation;
}
// All other animations use this basic animation
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:event];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.removedOnCompletion = YES;
animation.fillMode = kCAFillModeForwards;
animation.duration = 0.25;
return animation;
}
My transform is a translation followed by a rotate, but I think a group animation with separate keyframe animations animating through a translation AND a rotate would result in crazy town. I've confirmed that size & transform are correct for all values of p that I pass though, and p strictly ranges from 0 to 1.
I've tried setting a non-default timing function, I've tried setting an array of timing functions, I've omitted the keyTImes, I've set a repeatCount of 0, removedOnCompletion=YES, and a fillMode of forwards and that had no effect. Am I not creating the transform keyframe animation correctly?
This technique worked back in iOS 3 but seemed to be broken in iOS 5.0.
5.1 'magically' fixed this, it seemed to be a bug in iOS 5.0. I'd file a radar, but it is now working in 5.1.
#Gsnyder: Some background: I am experimenting with Clear-like UI (for something completely unrelated to Clear) and came up with this: http://blog.massivehealth.com/post/18563684407/clear. That should explain the need for a rotate & translate.
I've since created a shutter transition that subdivides a view into N layers (instead of just 2) that looks like this when viewed from the side: /////.
My code is not animating the bounds, it is using the size at each step to determine the necessary transform.
#Paul.s: Implicit allows me to keep this abstraction within the layer class itself without polluting the view controller that owns it. The view controller should just be changing the bounds around and the layer should move appropriately. I'm not a fan of view controllers having dozens of custom animations when the views themselves can handle it.
I need to use a keyframe animation because the default animation between layer transforms / and _ animate through incorrect angles so the ///\ layers do not line up throughout the transform. The keyframe animations ensure the edges all line up correctly while they all animate.
I'm considering this closed, this seems to be a bug in iOS 5.0 and has since been fixed. Thanks everyone.
(void)animateViewWith3DCurrentView:(UIView *)currentView withPoing:(CGPoint)movePoint
{
//flip the view by 180 degrees in its place first.
currentView.layer.transform = CATransform3DRotate(currentView.layer.transform,myRotationAngle(180), 0, 1, 0);
//set the anchor point so that the view rotates on one of its sides.
currentView.layer.anchorPoint = CGPointMake(0.5, 0.5);
//Set up scaling
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:kResizeKey];
//we are going to fill the screen here. So 423,337
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(423, 337)]];
resizeAnimation.fillMode = kCAFillModeForwards;
resizeAnimation.removedOnCompletion = NO;
// Set up path movement
UIBezierPath *movePath = [UIBezierPath bezierPath];
//the control point is now set to centre of the filled screen. Change this to make the path different.
// CGPoint ctlPoint = CGPointMake(0.0, 0.5);
CGPoint ctlPoint = CGPointMake(1024/2, 768/2);
//This is the starting point of the animation. This should ideally be a function of the frame of the view to be animated. Hardcoded here.
// Set here to get the accurate point..
[movePath moveToPoint:movePoint];
//The anchor point is going to end up here at the end of the animation.
[movePath addQuadCurveToPoint:CGPointMake(1024/2, 768/2) controlPoint:ctlPoint];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:kPathMovement];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
// Setup rotation animation
CABasicAnimation* rotateAnimation = [CABasicAnimation animationWithKeyPath:kRotation];
//start from 180 degrees (done in 1st line)
CATransform3D fromTransform = CATransform3DMakeRotation(myRotationAngle(180), 0, 1, 0);
//come back to 0 degrees
CATransform3D toTransform = CATransform3DMakeRotation(myRotationAngle(0), 0, 1, 0);
//This is done to get some perspective.
CATransform3D persp1 = CATransform3DIdentity;
persp1.m34 = 1.0 / -3000;
fromTransform = CATransform3DConcat(fromTransform, persp1);
toTransform = CATransform3DConcat(toTransform,persp1);
rotateAnimation.toValue = [NSValue valueWithCATransform3D:toTransform];
rotateAnimation.fromValue = [NSValue valueWithCATransform3D:fromTransform];
//rotateAnimation.duration = 2;
rotateAnimation.fillMode = kCAFillModeForwards;
rotateAnimation.removedOnCompletion = NO;
// Setup and add all animations to the group
CAAnimationGroup *group = [CAAnimationGroup animation];
[group setAnimations:[NSArray arrayWithObjects:moveAnim,rotateAnimation, resizeAnimation, nil]];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = 0.7f;
group.delegate = self;
[group setValue:currentView forKey:kGroupAnimation];
[currentView.layer addAnimation:group forKey:kLayerAnimation];
}

Animating a CALayer's mask size change

I have a UIView subclass which uses a CAShapeLayer mask on its CALayer. The mask uses a distinct shape, with three rounded corners and a cut out rectangle in the remaining corner.
When I resize my UIView using a standard animation block, the UIView itself and its CALayer resize just fine. The mask, however, is applied instantly, which leads to some drawing issues.
I've tried animating the mask's resizing using a CABasicAnimation but didn't have any luck getting the resizing animated.
Can I somehow achieve an animated resizing effect on the mask? Do I need to get rid of the mask, or will I have to change something about the way I currently draw the mask (using - (void)drawInContext:(CGContextRef)ctx).
Cheers,
Alex
I found the solution to this problem. Other answers are partially correct and are helpful.
The following points are important to understanding the solution:
The mask property is not animatable itself.
Since the mask is a CALayer it can be animated on its own.
Frame is not animatable, use bounds and position. This may not apply to you(if you weren't trying to animate the frame), but was an issue for me. (See Apple QA 1620)
A view layer's mask is not tied to UIView so it will not receive the core animation transaction that is applied to the view's layer.
We are modifying the CALayer directly, so we can't expect that UIView will have any idea of what we are trying to do, so the UIView animation won't create the core animation transaction to include changes to our properties.
In order to solve, we are going to have to tap into Core Animation ourselves, and can't rely on the UIView animation block to do the work for us.
Simply create a CATransaction with the same duration that you are using with [UIView animateWithDuration:...]. This will create a separate animation, but if your durations and easing function is the same, it should animate exactly with the other animations in your animation block.
NSTimeInterval duration = 0.5;// match this to the value of the UIView animateWithDuration: call
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:duration] forKey:kCATransactionAnimationDuration];
self.myView.layer.mask.position = CGPointMake(newX, 0);
self.myView.layer.mask.bounds = CGRectMake(0, 0, newWidth, newHeight);
[CATransaction commit];
I use a CAShapeLayer to mask a UIView by setting self.layer.mask to that shape layer.
To animate the mask whenever the size of the view changes I overwrote the -setBounds: to animate the mask layer path if the bounds are changed during an animation.
Here's how I implemented it:
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
CAPropertyAnimation *boundsAnimation = (CABasicAnimation *)[self.layer animationForKey:#"bounds"];
// update the mask
self.maskLayer.frame = self.layer.bounds;
// if the bounds change happens within an animation, also animate the mask path
if (!boundsAnimation) {
self.maskLayer.path = [self createMaskPath];
} else {
// copying the original animation allows us to keep all animation settings
CABasicAnimation *animation = [boundsAnimation copy];
animation.keyPath = #"path";
CGPathRef newPath = [self createMaskPath];
animation.fromValue = (id)self.maskLayer.path;
animation.toValue = (__bridge id)newPath;
self.maskLayer.path = newPath;
[self.maskLayer addAnimation:animation forKey:#"path"];
}
}
(For the example self.maskLayer is set to `self.layer.mask)
My -createMaskPath calculates the CGPathRef that I use to mask the view. I also update the mask path in -layoutSubviews.
The mask property of CALayer is not animatable which explains your lack of luck in that direction.
Does the drawing of your mask depend on the frame/bounds of the mask? (Can you provide some code?) Does the mask have needsDisplayOnBoundsChange property set?
Cheers,
Corin
To animate the bounds change of the mask layer of a UIView: subclass UIView, and animate the mask with a CATransaction - similar to Kekodas answer but more general:
#implementation UIMaskView
- (void) layoutSubviews {
[super layoutSubviews];
CAAnimation* animation = [self.layer animationForKey:#"bounds"];
if (animation) {
[CATransaction begin];
[CATransaction setAnimationDuration:animation.duration];
}
self.layer.mask.bounds = self.layer.bounds;
if (animation) {
[CATransaction commit];
}
}
#end
The mask parameter doesn't animate, but you can animate the layer which is set as the mask...
If you animate the CAShapeLayer's Path property, that should animate the mask. I can verify that this works from my own projects. Not sure about using a non-vector mask though. Have you tried animating the contents property of the mask?
Thanks,
Jon
I couldn't find any programmatical solution so I just draw an png image with correct shape and alpha values and used that instead. That way I don't need to use a mask...
It is possible to animate the mask change.
I prefer to use CAShapeLayer as the mask layer. It is very convenient to animate a mask change with the help of property path.
Before animate any change, dump the content of the source into an instance CGImageRef, and create a new layer for animation. Hide the original layer during the animation and show it when animation ends.
The following is a sample code for creating key animation on property path.
If you want to create your own path animation, make sure that there are always same number of points in the paths.
- (CALayer*)_mosaicMergeLayer:(CGRect)bounds content:(CGImageRef)content isUp:(BOOL)isUp {
CALayer* layer = [CALayer layer];
layer.frame = bounds;
layer.backgroundColor = [[UIColor clearColor] CGColor];
layer.contents = (id)content;
CAShapeLayer* maskLayer = [CAShapeLayer layer];
maskLayer.fillColor = [[UIColor blackColor] CGColor];
maskLayer.frame = bounds;
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.path = ( isUp ? [self _maskArrowUp:-bounds.size.height*2] : [self _maskArrowDown:bounds.size.height*2] );
layer.mask = maskLayer;
CAKeyframeAnimation* ani = [CAKeyframeAnimation animationWithKeyPath:#"path"];
ani.removedOnCompletion = YES;
ani.duration = 0.3f;
ani.fillMode = kCAFillModeForwards;
ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
NSArray* values = ( isUp ?
[NSArray arrayWithObjects:
(id)[self _maskArrowUp:0],
(id)[self _maskArrowUp:-ceilf(bounds.size.height*1.2)],
nil]
:
[NSArray arrayWithObjects:
(id)[self _maskArrowDown:0],
(id)[self _maskArrowDown:bounds.size.height],
nil]
);
ani.values = values;
ani.delegate = self;
[maskLayer addAnimation:ani forKey:nil];
return layer;
}
- (void)_startMosaicMergeAni:(BOOL)up {
CALayer* overlayer = self.aniLayer;
CGRect bounds = overlayer.bounds;
self.firstHalfAni = NO;
CALayer* frontLayer = nil;
frontLayer = [self _mosaicMergeLayer:bounds
content:self.toViewSnapshot
isUp:up];
overlayer.contents = (id)self.fromViewSnapshot;
[overlayer addSublayer:frontLayer];
}
Swift 3+ answer based on Kekoa's solution:
let duration = 0.15 //match this to the value of the UIView.animate(withDuration:) call
CATransaction.begin()
CATransaction.setValue(duration, forKey: kCATransactionAnimationDuration)
myView.layer.mask.position = CGPoint(x: [new X], y: [new Y]) //just an example
CATransaction.commit()
Swift implementation of #stigi answer, my mask layer is called shape Layer
override var bounds: CGRect {
didSet {
// debugPrint(self.layer.animationKeys()) //Very useful for know if animation is happening and key name
let propertyAnimation = self.layer.animation(forKey: "bounds.size")
self.shapeLayer?.frame = self.layer.bounds
// if the bounds change happens within an animation, also animate the mask path
if let boundAnimation = propertyAnimation as? CABasicAnimation {
// copying the original animation allows us to keep all animation settings
if let basicAnimation = boundAnimation.copy() as? CABasicAnimation {
basicAnimation.keyPath = "path"
let newPath = UIBezierPathUtils.customShapePath(rect: self.layer.bounds, cornersTriangleSize: cornerTriangleSize).cgPath
basicAnimation.fromValue = self.shapeLayer?.path
basicAnimation.toValue = newPath
self.shapeLayer?.path = newPath
self.shapeLayer?.add(basicAnimation, forKey: "path")
}
} else {
self.shapeLayer?.path = UIBezierPathUtils.customShapePath(rect: self.layer.bounds, cornersTriangleSize: cornerTriangleSize).cgPath
}
}
}

iPhone animation problem SImulator vs Real Device

This is my first question her so please be gentle:
I have followig animation code running smoothly on the simulator as well as on the real device (I am testng on iPhone 3GS 3.1.2).
Animation is a simple transition between the 2 views, something like book page flipping.
One diffrence betwen simulator an real device (The problem I cannot investigate - solve) is that on real device when animation finishes - after rotation has been done animated view blink (show for a split of second) for a moment before it goes hidden. On the simulator this 'unexpected' blink does not happen.
Here is the animation code:
-(void)flip{
UIView *animatedView;
// create an animation to hold the page turning
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
transformAnimation.removedOnCompletion = NO;
transformAnimation.delegate = self;
transformAnimation.duration = ANIMATION_TIME;
transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// this is the basic rotation by 90 degree along the y-axis
CATransform3D endTransform = CATransform3DMakeRotation(3.141f/2.0f,
0.0f,
-1.0f,
0.0f);
// these values control the 3D projection outlook
endTransform.m34 = 0.001f;
endTransform.m14 = -0.0015f;
// start the animation from the current state
transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
transformAnimation.toValue = [NSValue valueWithCATransform3D:endTransform];
animatedView = screenShot;
// Create an animation group to hold the rotation and possibly more complex animation in the future
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
// Set self as the delegate to receive notification when the animation finishes
theGroup.delegate = self;
theGroup.duration = ANIMATION_TIME;
// CAAnimation-objects support arbitrary Key-Value pairs, we add the UIView tag
// to identify the animation later when it finishes
[theGroup setValue:[NSNumber numberWithInt:animatedView.tag] forKey:#"animated"];
// Here you could add other animations to the array
theGroup.animations = [NSArray arrayWithObjects:transformAnimation,nil];
theGroup.removedOnCompletion = NO;
// Add the animation group to the layer
if (animatedView.layer.anchorPoint.x != 0.0f)
{
animatedView.layer.anchorPoint = CGPointMake(0.0f, 0.5f);
float yy = animatedView.center.x - (animatedView.bounds.size.width / 2.0f);
animatedView.center = CGPointMake(yy, animatedView.center.y);
}
if(![animatedView isDescendantOfView:self.view])[self.view addSubview:animatedView];
screenShot.hidden = NO;
animatedView.hidden = NO;
[animatedView.layer addAnimation:theGroup forKey:#"flip"];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
screenShot.hidden = YES;
}
Try setting theGroup's and / or transformAnimation's fillMode to kCAFillModeForwards:
theGroup.fillMode = kCAFillModeForwards;
This should cause your animation to persist once its active duration has completed. I've used this to remove an end-of-animation flicker before.
Not sure I understand completely what you are seeing but it's possible you're seeing something caused by the speed difference between the device and the simulator. The simulator is considerably faster than the device so something that "blinks" very quickly might do so too fast for the human eye to catch on the simulator.
Oof. That's annoying. I think it is that there's a split-second between when animation stops and when that line of code executes. If only there were an animationWillStop: method! You might try, instead of screenShot.hidden = YES, screenShot.alpha = 0. I doubt it will make any difference speed-wise, but it might be worth a shot? You could also fade out the view as part of the animation, but I don't think you want to do that.
The only other thing I can think of is to setup an NSTimer with an interval just under your animation time. Something like:
[NSTimer scheduledTimerWithTimeInterval:(ANIMATION_TIME - .001) target:self selector:#selector(hideTheView) userInfo:nil repeats:NO];
And then implement that hideTheView method, of course.