How to capture current view screenshot and reuse in code? (iPhone SDK)
I am attemting to transition from one UIView to another, when the user rotates the device. This, in of itself, is not difficult. However, since I am displaying completely different content after the rotation, the default animation provided by UIKit (rotating the currently displayed view) is inappropriate conceptually.
Simply disabling the animation and swapping the views suddenly is tolerable, but is far below the polish I'm building into the rest of the app. What I would prefer to do is this:
When shouldAutorotateToInterfaceOrientation: is called, I would like to grab an opaque view snapshot, a screenshot if you will, of the user view before rotation. Then after the rotation is completed and the system has applied the view transforms etc, I can show the snapshot view I saved and animate a transition of my choice to my new view. After it is completed, I can release my snapshot and move on.
Is there a way to do this that is not expensive?
The only other option I can think of is to return NO on all orientations other than my default one, and then react by applying my own animations and transforms. I'd prefer to use the system to do this however, as I feel it is likely doing it myself could cause "undefined" keyboard behavior in the manually rotated view, etc.
Thoughts?
- (UIImage *)captureView:(UIView *)view {
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(screenRect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, screenRect);
[view.layer renderInContext:ctx];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Found this here :
http://discussions.apple.com/thread.jspa?messageID=8358740
This code worked for me
- (UIImage *)captureScreenInRect:(CGRect)captureFrame {
CALayer *layer;
layer = self.view.layer;
UIGraphicsBeginImageContext(self.view.bounds.size);
CGContextClipToRect (UIGraphicsGetCurrentContext(),captureFrame);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenImage;
}
On Mac this is so easy... you just need -bitmapImageRepForCachingDisplayInRect:
, but of course iPhone has no such thing.
I think I would attack this by setting up a graphics context and calling [window drawRect:...]
on it to capture the current image of the screen. This is a bit out of spec, because you're not really supposed to go calling -drawRect:
yourself. I'd be a bit nervous of side-effects on doing this, but it may be fine. If it's a full-screen view you control, you could of course hoist the drawing code out of -drawRect:
so you're not calling that directly.
A similar approach would be to use CALayer -drawInContext:
of the current -presentationLayer
. That might be slightly more "in spec" since it's legal to pass that your own context. But I don't know if you'd actually be able to get all the sub-views correctly. Might depend on the nature of your UIWindow.
Anyway, great question. Maybe someone has a more clever solution, but this is the way I'd attack it.
EDIT: Thanks to Rob Terrell, and his clever UIApplication+TVOut.m, I was introduced to the private method UIGetScreenImage() which returns a CGImageRef. If you're willing to go to private methods, that's a nice option for you. Given its name and functionality, it looks pretty stable to me and seems unlikely to violate Apple's intent. I also bumped into a pretty nice discussion on Air Source. Read through the comments for several links.