Render layer of UIView which is not in the View Hierarchy - uiview

What i want to achieve is to take an image of an UIView which has not been added as a subview, present and do stuff with the image and afterwards add the view to the view hierarchy.
I've searched and tried now for a while and believe, that it is simply not possible.
Obviously the problem is, that the view hasn't been drawn (called drawRect: i guess) if it hasn't been added as a subview.
Actually i thought renderInContext: would call drawRect/layer on its own.
It isn't even enough to add it as subview right before draw it to an imageContext because it won't be rendered immediately.
I take the screenshot with renderInContext: with the layer of the view, see my code here:
[self.view addSubView:view];
UIGraphicsBeginImageContextWithOptions(view.frame.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, -frame.origin.x, -frame.origin.y);
[view.layer renderInContext:context];
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
So my question is, has anybody managed to render a not visible UIView and if how?

Well this is awkward.
After a mail conversation with a very kind apple dev support, we reviewed my code and we noticed that i simply set the hidden property to YES. - Just don't do that.
So it is straight forward to make a screenshot of a view which is not in the view hierarchy.
It was total my fault why it didn't work.

Try to addd UIView to hierarchy but keep it hidden.
- (void)takeScreenSnapshot {
UIView *capturedView = self.view;
UIView *hiddenView = self.hiddeniew; // hidden view which is
// a part of capturedView
hiddenView.hidden = NO;
BOOL retina = [self isRetinaDisplay];
UIImage *image = [capturedView captureImageWithScale:(retina) ? 2.f : 1.f];
hiddenView.hidden = YES;
}

Related

iOS - screenshot of a hidden UIView

I have a hidden UIView that I need to take a full screenshot of. I don't want to .hidden = NO * take screenshot * .hidden = YES. The reason is because that causes an inverted looking screenshot sometimes.
+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
This is my code, it does nothing if the UIView is hidden.
Take a snapshot of a hidden UIView -- Doesn't work
I've googled around and haven't found an answer. Please respond with code in Objective-C.
from your given link, the another view should ONLY cover your view and then set hidden to false/NO fro your view meaning your view should be visible behind the cover view. That's working perfectly. keep your view you want to draw at bottom in view hierarchy and the cover view covering your view.

Fastest way to take screenShot of UIView

I've searched a lot but only found two methods to take screen shot of UIView.
first renderInContext:
I've used it in a way
CGContextRef context = [self createBitmapContextOfSize:CGSizeMake(nImageWidth, nImageHeight)];
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, nImageHeight);
CGContextConcatCTM(context, flipVertical);
[self.layer setBackgroundColor:[UIColor clearColor].CGColor];
[self.layer renderInContext:context];
CGImageRef cgImage = CGBitmapContextCreateImage(context);
UIImage* background = [UIImage imageWithCGImage: cgImage];
CGImageRelease(cgImage);
Second drawViewHierarchyInRect: which I've used as
UIImage *background = nil;
UIGraphicsBeginImageContextWithOptions (self.bounds.size, NO, self.window.screen.scale);
if ([self respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)])
{
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
}
background = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I know that the second one is faster than first and it work for me for iPhone because the view has low size. but when I capturing from iPad the video become jerky.
Can Any body tell me faster way of taking screen shot.
any help would be highly appreciated
Regarding performance, the Apple Docs state the following:
In addition to -drawViewHierarchyInRect:afterScreenUpdates:, UIView
now provides another two snapshot related methods,
-snapshotViewAfterScreenUpdates: and -resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets:. UIScreen also has -snapshotViewAfterScreenUpdates:.
Unlike UIView's -drawViewHierarchyInRect:afterScreenUpdates:, these
methods return a UIView object. If you are looking for a new snapshot
view, use one of
these methods. It will be more efficient than calling
-drawViewHierarchyInRect:afterScreenUpdates: to render the view contents into a bitmap image yourself. You can use the returned view
as a visual stand-in for the current view/screen in your app. For
example, you might use a snapshot view for animations where updating a
large view hierarchy might be expensive.
There is a third method for taking a snapshot that is much much quicker than either of these but it returns a UIView.
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
If you are just using the snapshot to place as a background "image" etc... then I'd use this instead.
However, this is only available for iOS8.
To use it just do...
UIView *snapshotView = [someView snapshotViewAfterScreenUpdates:YES];
This Method will return you A snapshot images of particular view
-(UIImage *)createSnapShotImagesFromUIview
{
UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width,view.frame.size.height));
CGContextRef context = UIGraphicsGetCurrentContext();
[mapView.layer renderInContext:context];
UIImage *img_screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img_screenShot;
}

Screenshot of iPad detail view orientation wrong

I'm trying to capture a screenshot of the detail view in a landscape master/detail layout on iPad.
This is the code I've tried using.
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
CGRect rect = [self.view bounds];
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[keyWindow.layer renderInContext:context];
UIImage *capturedScreen = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Two problems occur with this.
- the screen capture orientation is incorrect. I get an image that is on it's side.
- The width=703 & height=768 dimensions are reversed by the screen capture so I end up with some of the master view in the detail screen shot.
What am I doing wrong here? Thanks!
try this way
-(UIImage *)captureScreenForRect:(CGRect)frame
{
CALayer *layer;
layer = self.view.layer;
UIGraphicsBeginImageContext(self.view.bounds.size);
CGContextClipToRect (UIGraphicsGetCurrentContext(),self.view.bounds);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenImage;
}
pass your detail view frame rect for above method. hope this will help you
The "official" screenshot method is here:
(https://developer.apple.com/library/ios/qa/qa1703/_index.html)
If you need this for a transition or for a graphical effect and your using iOS 7 - I suggest you don't actually create an image.
An image can be heavy to generate (for example on iPAd Retina 3rd Gen) and heavy on the memory.
Starting iOS 7 Apple gives you a much quicker Snapshot function on UIView (Which by the way is also the way they implement custom transitions in view controllers , blur effect etc) that is done much quicker then creating an actual image.
On a UIView you can perform:
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
Or if you need the full view hierarchy for a blurred view:
- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates

iOS7's drawViewHierarchyInRect doesn't work?

From what I've read, iOS7's new drawViewHierarchyInRect is supposed to be faster than CALayer's renderInContext. And according to this and this, it should be a simple matter of calling:
[myView drawViewHierarchyInRect:myView.frame afterScreenUpdates:YES];
instead of
[myView.layer renderInContext:UIGraphicsGetCurrentContext()];
However, when I try this, I just get blank images. Full code that does the capture, where "self" is a subclass of UIView,
// YES = opaque. Ignores alpha channel, so less memory is used.
// This method for some reasons renders the
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale); // Still slow.
if ( [AIMAppDelegate isOniOS7OrNewer] )
[self drawViewHierarchyInRect:self.frame afterScreenUpdates:YES]; // Doesn't work!
else
[self.layer renderInContext:UIGraphicsGetCurrentContext()]; // Works!
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
contentImageView.image = image; // this is empty if done using iOS7's way
and contentImageView is a UIImageView that is added as a subView to self during initialization.
Additionally, the drawing that I want captured in the image is contained in other sub-views that are also added to self as a sub-view during initialization (including contentImageView).
Any ideas why this is failing when using drawViewHierarchyInRect?
* Update *
I get an image if I draw a specific sub-view, such as:
[contentImageView drawViewHierarchyInRect:contentImageView.frame afterScreenUpdates:YES];
or
[self.curvesView drawViewHierarchyInRect:self.curvesView.frame afterScreenUpdates:YES];
however I need all the visible sub-views combined into one image.
Try it with self.bounds rather than self.frame—it’s possible you’re getting an image of your view rendered outside the boundaries of the image context you’ve created.

Taking a screenshot of a view that is currently not on screen on iOS

I'm trying to make a transition between two ViewControllers. My Transition class has a property for the destination view controller. When I try to get a screenshot of the destination's view, I use this method:
+ (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame {
// Create a new context the size of the frame
UIGraphicsBeginImageContextWithOptions(frame.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Render the view
[view.layer renderInContext:context];
// Get the image from the context
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
// Cleanup the context you created
UIGraphicsEndImageContext();
return renderedImage;
}
So when I want the image, I'll do this:
UIImage *image = [UIImage renderImageFromView:destinationController.view withRect:destinationController.view.bounds];
I tried it on a blank UIViewController with an orange background color and a label. I get the orange background color, but I do not get the label that was created in IB. Is there a way to get a proper screenshot of the new view I plan on showing? Thanks!
You need to make sure the view's layer is drawn to first. Calling renderInContext on a CALayer will only recursively call additional child CALayers. You need your child UIViews to draw themselves, not just using the CALayer.
Try calling
[view drawrect:frame];
instead of
[view.layer renderInContext:context];
As long as you have an open graphics context(which you do at that point), drawrect should draw directly into that.

Resources