I'm trying to make a push animation from one side using a CATransition
. I do this in method which handles two UIViews
, one for the background and another for the foreground. The animated UIView
is be the foreground. However I don't want the new content to replace the old one, so to do this I'm moving all the old content from the foreground to the background UIView
expecting that it will just animate the push transition of the UIView
from one side, but instead it replaces the old UIView
. Is there any way to do what I'm attempting to do, something like locking the state of the UIViews
from the moment where the foreground and background UIViews
have the right content?
My code is something like this:
UIView *newView = argView; // argView is the view passed as a parameter to the method
// move views from foreground to background
for (UIView *v in [fgView subviews]) {
[v removeFromSuperview];
[bgView addSubview:v];
}
// Slide animation
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[fgView.layer addAnimation:transition forKey:nil];
[fgView addSubview:newView];
[edit] I've been doing some research. Apparently (or what I think might be happening) is that the background's UIViews removal are adopting implicit animation. Is there a way to stop it from animating?
[edit] Answer: Yes, it can be done (disabling the implicit animation) and it is done with the following code
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
for (UIView *v in [fgView subviews]) {
[v removeFromSuperview];
[bgView addSubview:v];
}
[CATransaction commit];
This is what I did and it should work (according to apple's developer portal) but it doesn't, so the question is still open.
[edit] After a lot of testing and reading I have found out that the problem is that when I remove a view from its superview the Transition or Animation does not recognizes this change until later, so it responds outdated of the latest changes. Is there any way to force an update or to check when a removeFromSuperview
or addSubview
is finished?
[edit] What I need exactly is this: 1) the current state of the UIViews is: bgView with old views behind a shaded UIView or midView (this view shouldn't be animated). A midView which is NEVER animated. A fgView with the current UIView. 2) I want to move the contents of fgView to bgView (which won't be animated) and animate the appearance of a new UIView which is added to fgView (using Slide, Fade in/out or Flip animations).
Maybe the better way is to make a "screenshot" of your old content and add the screenshot as a UIImageView, then you can change your UIView's content to the new one.
After this, you can easily add animations to the two views and there is no need to worry about the old content be changed.
You can add the two files below into your project and get a UIView's screenshot by invoke UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithUIView:view]];
As it's a UIImageView, so you don't have to worry about the content of the view will be changed.
UIImage+ImagewithUIView.h
@interface UIImage (UIImage+ImagewithUIView)
+ (UIImage *)imageWithUIView:(UIView *)view;
@end
UIImage+ImagewithUIView.m
@implementation UIImage (UIImage+ImagewithUIView)
#pragma mark -
#pragma mark TakeScreenShot
static CGContextRef createBitmapContext(int pixelsWide, int pixelsHigh)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = (kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGContextRef bitmapContext = CGBitmapContextCreate(nil, pixelsWide, pixelsHigh, 8, 0, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
return bitmapContext;
}
+ (UIImage *)imageWithUIView:(UIView *)view
{
CGSize screenShotSize = view.bounds.size;
CGContextRef contextRef = createBitmapContext(screenShotSize.width, screenShotSize.height);
CGContextTranslateCTM (contextRef, 0, screenShotSize.height);
CGContextScaleCTM(contextRef, 1, -1);
[view.layer renderInContext:contextRef];
CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
CGContextRelease(contextRef);
UIImage *img = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
// return the image
return img;
}
@end
UIView *newView = argView; // argView is the view passed as a parameter to the method
// move views from foreground to background
[fgView.layer removeAllAnimations]; // make sure all, if any, animations are removed.
for (UIView *v in [fgView subviews])
{
// [v removeFromSuperview]; // this should not be needed addSubview does it for you
[bgView addSubview:v];
}
// Slide animation
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
transition.removedOnCompletion = YES; // force removal of animation when completed.
[fgView addSubview:newView];
[fgView.layer addAnimation:transition forKey:nil];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With