I'm trying to capture the screen inside of my app, it used to work just great, but now I only get a blurry image. like that:
This is my code:
- (UIImage *)takeScreenShotWithFrame:(CGRect)frame
{
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(frame.size, NO, [UIScreen mainScreen].scale);
} else {
UIGraphicsBeginImageContext(frame.size);
}
[self.view drawViewHierarchyInRect:frame afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
What am I doing wrong?
Thanks in advance!
instead of:
[self.view drawViewHierarchyInRect:frame afterScreenUpdates:NO];
Try:
CGContextRef context = UIGraphicsGetCurrentContext();
[self.view.layer renderInContext:context];
Related
Using new API's like
snapshotViewAfterScreenUpdates
resizableSnapshotViewFromRect
drawViewHierarchyInRect
Using these i want to take photo in any format and need to send.
try this
UIView *screenshotView = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
UIImage *snapshotImage = [self imageFromView:screenshotView];
- (UIImage *)imageFromView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0.0);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
I want to take screenshot of only image in imageview. I have tried following code but it will take screenshot of imageview .
CGRect grabRect = CGRectMake(self.vwBackup.frame.origin.x,self.vwBackup.frame.origin.y,self.vwBackup.frame.size.width,self.vwBackup.frame.size.height);
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(grabRect.size, NO, [UIScreen mainScreen].scale);
} else {
UIGraphicsBeginImageContext(grabRect.size);
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -grabRect.origin.x, -grabRect.origin.y);
[self.view.layer renderInContext:ctx];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
self.ImgEditableView.image=viewImage;
Here i have attached image of taken screenshot with above code:
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)])
UIGraphicsBeginImageContextWithOptions(self.displayImageView.bounds.size, NO, [UIScreen mainScreen].scale);
else
UIGraphicsBeginImageContext(self.displayImageView.bounds.size);
[self.displayImageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * imgData = UIImagePNGRepresentation(image);
if(imgData)
{
[imgData writeToFile:#"screenshot.png" atomically:YES];
}
else
NSLog(#"error while taking screenshot");
I am using following code to capture screen. Before it was working fine but now its retuning nil image.I am using iPhone 5,5c and 5s.
+ (UIImage *)screenshot
{
CGSize imageSize = CGSizeZero;
imageSize = [UIScreen mainScreen].bounds.size;
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
for (UIWindow *window in [[UIApplication sharedApplication] windows])
{
CGContextSaveGState(context);
CGContextTranslateCTM(context, window.center.x, window.center.y);
CGContextConcatCTM(context, window.transform);
CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);
if ([window respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)])
{
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
}
else
{
[window.layer renderInContext:context];
}
CGContextRestoreGState(context);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
Now I am using this code and its working good.
+ (UIImage *)screenshot:(UIView *)view
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContextWithOptions(screenRect.size, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, screenRect);
[view.layer renderInContext:ctx];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Here is a method that I use to capture a UIView into a UIImage.
+ (UIImage *)renderView:(UIView *)view toImageWithSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.);
CGContextRef c = UIGraphicsGetCurrentContext();
[view.layer renderInContext:c];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
And then you can have your screenshot method that uses the method above to render the rootViewController of the main window into an image:
+ (UIImage *)screenshot {
UIWindow *mainWindow = [UIApplication sharedApplication].windows.firstObject;
UIView *mainView = mainWindow.rootViewController.view;
return [self renderView:mainView toImageWithSize:mainView.bounds.size];
}
I hope this is helpful.
Well, this piece of code works for me. This code capture a screenshot of the view (self.view here) and all its subviews. Means, in one word, screenshot of the screen.
+ (UIImage *)screenshot {
CGRectMake *captureFrame = [[UIScreen mainScreen] bounds]; //The portion and size of the area, you want to capture. I have set it to the size of window
CALayer *layer;
layer = self.view.layer;
UIGraphicsBeginImageContext(self.view.bounds.size);
CGContextClipToRect (UIGraphicsGetCurrentContext(),captureFrame);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenImage;
}
Hope this helps.
I am trying to generate screenshot of UIView which having subview with CATransform3DMakeRotation. Screenshot is generated but it doesn't contain Rotation.
Is it possible to achieve this?
Actual View:
ScreenShot Image
Using following call to Flip the view horizontally...
currentView.layer.transform = CATransform3DConcat(currentView.layer.transform,CATransform3DMakeRotation(M_PI, 0.0, 1.0, 0.0f));
Code for taking screen shot
+ (UIImage *) imageWithView:(UIView *)view
{
CGSize screenDimensions = view.bounds.size;
// Create a graphics context with the target size
// (last parameter takes scale into account)
UIGraphicsBeginImageContextWithOptions(screenDimensions, NO, 0);
// Render the view to a new context
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
The "renderInContext" only works for Affine transform. So convert the 3D transform into affine transform like this
currentView.layer.affineTransform = CATransform3DGetAffineTransform(CATransform3DConcat(currentView.layer.transform,CATransform3DMakeRotation(M_PI, 0.0, 1.0, 0.0f)));
Try this code
CGSize newSize = CGSizeMake(yourview.frame.size.width , yourview.frame.size.height);
UIGraphicsBeginImageContextWithOptions(newSize,YES,2.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
[yourview.layer renderInContext:context];
[yourview drawRect:yourview.frame];
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
This might work, try it:
CGRect grabRect = CGRectMake(40,40,300,200);
//for retina displays
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(grabRect.size, NO, [UIScreen mainScreen].scale);
} else {
UIGraphicsBeginImageContext(grabRect.size);
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -grabRect.origin.x, -grabRect.origin.y);
[self.view.layer renderInContext:ctx];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
i have achieved this in one of my application by doing a little tweak like first i capture the whole screen's screen shot and then crop it with the desired frame i need here is a sample code from my app.
- (UIImage *) croppedPhoto
{
[imgcropRectangle setHidden:TRUE];
UIGraphicsBeginImageContext(self.view.frame.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Create bitmap image from original image data,
// using rectangle to specify desired crop area
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], self.imgcropRectangle.frame);
UIImage *result = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
[imgcropRectangle setHidden:FALSE];
return result;
}
here imgcropRectangle is the UIImageView's object that defines my desired rectangle so i use it's frame for cropping from full screen to desired output. Hope it will help you :)
Try rendering view.layer.presentationLayer instead of view.layer
use this and before passing the view check its subviews :
+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width, view.frame.size.height));
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage
}
in my iPad app, I'd like to make a screenshot of a UIView taking a big part of the screen. Unfortunately, the subviews are pretty deeply nested, so it takes to long to make the screenshot and animate a page curling afterwards.
Is there a faster way than the "usual" one?
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
If possible, I'd like to avoid caching or restructuring my view.
I've found a better method that uses the snapshot API whenever possible.
I hope it helps.
class func screenshot() -> UIImage {
var imageSize = CGSize.zero
let orientation = UIApplication.shared.statusBarOrientation
if UIInterfaceOrientationIsPortrait(orientation) {
imageSize = UIScreen.main.bounds.size
} else {
imageSize = CGSize(width: UIScreen.main.bounds.size.height, height: UIScreen.main.bounds.size.width)
}
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
for window in UIApplication.shared.windows {
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
Wanna know more about iOS 7 Snapshots?
Objective-C version:
+ (UIImage *)screenshot
{
CGSize imageSize = CGSizeZero;
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsPortrait(orientation)) {
imageSize = [UIScreen mainScreen].bounds.size;
} else {
imageSize = CGSizeMake([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);
}
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
CGContextSaveGState(context);
CGContextTranslateCTM(context, window.center.x, window.center.y);
CGContextConcatCTM(context, window.transform);
CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);
if (orientation == UIInterfaceOrientationLandscapeLeft) {
CGContextRotateCTM(context, M_PI_2);
CGContextTranslateCTM(context, 0, -imageSize.width);
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
CGContextRotateCTM(context, -M_PI_2);
CGContextTranslateCTM(context, -imageSize.height, 0);
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
CGContextRotateCTM(context, M_PI);
CGContextTranslateCTM(context, -imageSize.width, -imageSize.height);
}
if ([window respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
} else {
[window.layer renderInContext:context];
}
CGContextRestoreGState(context);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
EDIT October 3. 2013
Updated to support the new super fast drawViewHierarchyInRect:afterScreenUpdates: method in iOS 7.
No. CALayer's renderInContext: is as far as I know the only way to do this. You could create a UIView category like this, to make it easier for yourself going forward:
UIView+Screenshot.h
#import <UIKit/UIKit.h>
#interface UIView (Screenshot)
- (UIImage*)imageRepresentation;
#end
UIView+Screenshot.m
#import <QuartzCore/QuartzCore.h>
#import "UIView+Screenshot.h"
#implementation UIView (Screenshot)
- (UIImage*)imageRepresentation {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale);
/* iOS 7 */
if ([self respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)])
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
else /* iOS 6 */
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* ret = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return ret;
}
#end
By this you might be able to say [self.view.window imageRepresentation] in a view controller, and get a full screenshot of your app. This might exclude the statusbar though.
EDIT:
And may I add. If you have an UIView with transparent content, and needs an image representation WITH the underlaying content as well, you can grab an image representation of the container view and crop that image, simply by taking the rect of the subview and converting it to the container views coordinate system.
[view convertRect:self.bounds toView:containerView]
To crop see answer to this question: Cropping an UIImage
iOS 7 introduced a new method that allows you to draw a view hierarchy into the current graphics context. This can be used to get an UIImage very fast.
Implemented as category method on UIView:
- (UIImage *)pb_takeSnapshot {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
It is considerably faster then the existing renderInContext: method.
UPDATE FOR SWIFT: An extension that does the same:
extension UIView {
func pb_takeSnapshot() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.mainScreen().scale);
self.drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
// old style: self.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
}
I combined the answers to single function which will be running for any iOS versions, even for retina or non-retains devices.
- (UIImage *)screenShot {
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)])
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, [UIScreen mainScreen].scale);
else
UIGraphicsBeginImageContext(self.view.bounds.size);
#ifdef __IPHONE_7_0
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
[self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES];
#endif
#else
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
#endif
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
For me setting the InterpolationQuality went a long way.
CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);
If you are snapshotting very detailed images this solution may not be acceptable. If you are snapshotting text you will hardly notice the difference.
This cut down the time to take the snap shot significantly as well as making an image that consumed far less memory.
This is still beneficial with the drawViewHierarchyInRect:afterScreenUpdates: method.
What you’re asking for as an alternative is to read the GPU (since the screen is composited from any number of translucent views), which is an inherently slow operation too.