I have a view which is placed at the particular transform. i want to take a screenshot of that view but after render it not taking the value of transform.
My View
dragView = [[DragbleView alloc] initWithFrame:CGRectMake(10, 200, 200, 90)];
dragView.contentView = textField;
dragView.transform = CGAffineTransformMakeRotation (-0.663247);
[self.view addSubview:dragView];
My Screenshot Code
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, [UIScreen mainScreen].scale);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
Problem After taking screenshot it always comes horizontal neglecting the value of transform
You can take a snapshot of the parent view of all subviews that you want to keep in the snapshot image, the parent view is self.view. Then the transform of dragView is kept.
UIImage *mainImage = [self imageWithView:self.view];
And you forget UIGraphicsEndImageContext() in your btnSaveClicked function.
Related
I'm using -drawViewHierarchyInRect:afterScreenUpdates: to draw a view into a UIImage, but it seems no matter what I do the results is always a very low resolution, because the image seems to need to be the same size as the original view?
The UIView is the size of the screen (in this case, 375 x 667), and the content scale factor is 2.0f.
But when I use -drawViewHierarchyInRect:afterScreenUpdates: to draw the view onto the image, it is resized (horribly) to this resolution, even though the contents is a much higher resolution.
I'm using UIGraphicsBeginImageContextWithOptions with a scale of 0.0 to create the image, so I assumed it should draw nicely, but no go. How do you draw the view hierarchy and not lose the detail?
Edit:
Here's some example code (from the answer below, which is giving the same result). It's giving me terribly down-scaled results when trying to draw the UIView hierarchy which includes a high res image:
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0);
[self.view drawViewHierarchyInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
imgView.frame = self.view.bounds;
[self.view addSubview:imgView];
[self.view bringSubviewToFront:imgView];
This code works perfectly. It takes screenshot of current view and puts it to new created UIImageView. Please check that
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0);
[self.view drawViewHierarchyInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
imgView.frame = self.view.bounds;
[self.view addSubview:imgView];
[self.view bringSubviewToFront:imgView];
I am first to use UIGraphicsBeginImageContext.
I want to capture the visible part of the tableView to an UIImage and let it to the content of a new CALayer.
Here is my code:
UIGraphicsBeginImageContext(tableView.frame.size);
[tableView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect frame = [self.view.window convertRect:view.frame fromView:tableView.superview];
CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (id)image.CGImage;
imageLayer.frame = frame;
However, if I scroll down the tableView, the top part of captured image will be white.
I use UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); to see the result.
After longtime finding the answer, I have to ask this question here, hope someone can help me!
Thanks!
Make a view called "viewHolder", set it's "clipsToBounds" to yes. Add tableView as a subview to viewHolder. Now use:
UIGraphicsBeginImageContext(viewHolder.frame.size);
[viewHolder.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
In my app I have a scrollview with an added subview called allView.
In the scrollview delegate methods I am applying value of the current transformation of the scrollview's subview to another view called paint view
paintView.transform = allView.transform
and save it to the disk.
The image that is created in result of that process looks different than the one on the screen. Why? How can I fix it?
View Controller
- (void)scrollViewDidScroll:(UIScrollView *)scrollView; {
self.paintView.transform =self.allView.transform;
[self.paintView setNeedsDisplay];
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
self.backgroundView.transform = self.allView.transform;
[self.paintView setNeedsDisplay];
}
Paint View
Inside the PaintView's draw rect I am trying to apply transformation from the scroll view and
- (void)drawRect:(CGRect)rect
{
// Drawing code
// Draw on the screen
CGContextRef ctx1 =UIGraphicsGetCurrentContext();
CGContextConcatCTM(ctx1, self.transform);
CGColorRef wh = [[UIColor redColor]CGColor];
CGContextSetStrokeColorWithColor(ctx1, wh);
CGContextMoveToPoint(ctx1, 0, 0);
CGContextAddLineToPoint(ctx1, 200, 200);
CGContextStrokePath(ctx1);
// Apply scroll view's transformation
CGRect r = CGRectApplyAffineTransform(rect,self.transform );
//that gives a resized image
UIGraphicsBeginImageContextWithOptions(r.size, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextConcatCTM(ctx, self.transform);
// stroke and so on
CGContextSetStrokeColorWithColor(ctx, wh);
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, 200, 200);
CGContextStrokePath(ctx);
Getting image with entire content.
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//Clipping the image
CGImageRef cgImg = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *img = [UIImage imageWithCGImage:cgImg];
NSData * d = UIImageJPEGRepresentation(img, 0.8);
//saving the image (for debugging)
[self save:d];
UIGraphicsEndImageContext();
}
iOS Simulator
Image saved to the disk
It may be a little bit hard to diagnose the problem here without being able to have running code. However, I think the problem may be on these lines where you are clipping the image:
// Clipping the image
CGImageRef cgImg = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *img = [UIImage imageWithCGImage:cgImg];
I think you actually just want to get the scroll view's visible rect, which would be scrollView.frame rather than the subview that fills the scroll view's entire contentSize. So, using some way (such as a property on paintView such as a visibleFrame), I would revise the lines to be something like this:
// Clipping the image
CGImageRef cgImg = CGImageCreateWithImageInRect(image.CGImage, self.visibleFrame);
UIImage *img = [UIImage imageWithCGImage:cgImg];
Hope this helps you get passed this issue!
I am trying to render a view and it's contents after a transformation using UIGraphicsGetImageFromCurrentImageContext. Even though the view is scaled down, when I render it, the image size is correct, but the contents are rendered at their original size (in this case they are cropped as opposed to fit to the view as expected.
here is a simple sample project: http://owolf.net/uploads/StackOverflow/ParentChildTransformTester.zip
To explain further: I create a parent view and a child view, add the child as a subview. Then I transform scale the parent view, which resizes the child as well (just as I want). Then I create an image context at the current frame size of the parent view, and render. Code below.
//create views
UIView *parentView = [[UIView alloc]initWithFrame:self.view.bounds];
[parentView setBackgroundColor:[UIColor orangeColor]];
[self.view addSubview:parentView];
UIView *childView = [[UIView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
[childView setBackgroundColor:[UIColor redColor]];
[parentView addSubview:childView];
//log parent view size : result is device screen size
NSLog(#"parentView.frame.size, BEFORE tran: %f x %f", parentView.frame.size.width, parentView.frame.size.height);
float scaleFactor = 0.2;
CGAffineTransform trans = CGAffineTransformScale(CGAffineTransformIdentity, scaleFactor, scaleFactor);
parentView.transform = trans;
//log parent view size after transform : result is device screen size * 0.2
NSLog(#"parentView.frame.size, AFTER tran: %f x %f", parentView.frame.size.width, parentView.frame.size.height);
//attemt to render and save scaled parent view.
CGSize renderSize = CGSizeMake(parentView.frame.size.width, parentView.frame.size.height);
UIGraphicsBeginImageContext(CGSizeMake(renderSize.width, renderSize.height));
[[parentView layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *renderedImage;
renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(renderedImage, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
You need to apply the transformation to the current transformation matrix before you render the layer:
...
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextConcatCTM(ctx, [[parentView layer] affineTransform]);
[[parentView layer] renderInContext:ctx];
...
This worked: modified according to Graver's answer above.
UIGraphicsBeginImageContext(parentView.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextConcatCTM(ctx, [[parentView layer] affineTransform]);
[[parentView layer] renderInContext:UIGraphicsGetCurrentContext()];
renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(renderedImage, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
I'm trying to get a snapshot of another controller's view to use in an animation. I'm using the following code, but I see nothing but the background color of that view, and none of its subviews (two buttons, a text view, and a label):
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
FirstPageController *fpController = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstPage"];
UIGraphicsBeginImageContext(fpController.view.bounds.size);
NSLog(#"%#",fpController.view.subviews);
[fpController.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGSize destinationSize = CGSizeMake(90, 122);
UIGraphicsBeginImageContext(destinationSize);
[viewImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView2 = [[UIImageView alloc] initWithImage:newImage];
self.imageView2.center = self.view.center;
[self.view addSubview:self.imageView2];
NSLog(#"%#",NSStringFromCGSize(self.imageView2.image.size));
}
I've logged all the relevant things, the fpController, the imageView, the image, and all are logging correctly.
After Edit: If I switch to the current controller's view, it works fine, so I'm thinking that this has to do with getting a snapshot of a view that's not on screen. Can that be done?
Hope this will help you:
CGSize textcontent =CGSizeMake(width, height);
UIGraphicsBeginImageContext(textcontent);
if ([UIScreen instancesRespondToSelector:#selector(scale)] && [[UIScreen mainScreen] scale] == 2.0f)
{
UIGraphicsBeginImageContextWithOptions(textcontent, YES, 1.0f);
}
else
{
UIGraphicsBeginImageContext(textcontent);
}
[image.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
UIGraphicsBeginImageContextWithOptions(fpController.view.bounds.size, NO, 0.0);
UIGraphicsBeginImageContextWithOptions(destinationSize, NO, 0.0);
Use this two lines instead of UIGraphicsBeginImageContext(fpController.view.bounds.size);
UIGraphicsBeginImageContext(destinationSize); respectively
hope it help's you
I found one way to make this work, though it seems like a hack. I add the view I want to render as a subview of my view, do the rendering, and then remove the view. It is never seen on screen, so visually, it's fine. I just wonder whether there's a better way.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.fpController = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstPage"];
[self.view insertSubview:self.fpController.view belowSubview:self.view];
UIGraphicsBeginImageContext(self.fpController.view.bounds.size);
[self.fpController.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.fpController.view removeFromSuperview];
self.imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 90, 122)];
self.imageView2.image = viewImage;
[self.pageView insertSubview:self.imageView2 belowSubview:self.imageView];
}