Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create CATransition for UIView from current state

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).

like image 968
jglievano Avatar asked Feb 24 '23 05:02

jglievano


2 Answers

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
like image 74
xuzhe Avatar answered Mar 06 '23 20:03

xuzhe


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];
like image 40
tidwall Avatar answered Mar 06 '23 20:03

tidwall