How to take screenshot of UIScrollView visible area? - ios

How do I take a 1:1 screenshot of UIScrollView visible area? The content may be larger or smaller than UIScrollView bounds as well as half-hidden (I've implemented custom scrolling for smaller content, so it's not in the top-left corner).
I've achieved desired result on simulator, but not on device itself:
-(UIImage *)imageFromCombinedContext:(UIView *)background {
UIImage *image;
CGRect vis = background.bounds;
CGSize size = vis.size;
UIGraphicsBeginImageContext(size);
[background.layer affineTransform];
[background.layer renderInontext:UIGraphicsGetCurrentContext()];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRef imref = CGImageCreateWithImageInRect([image CGImage], vis);
image = [UIImage imageWithCGImage:imref];
CGImageRelease(imref);
return image;
}

Another approach would be to use the contentOffset to adjust the layer's visible area and capture only the currently visible area of UIScrollView.
UIScrollView *contentScrollView;....//scrollview instance
UIGraphicsBeginImageContextWithOptions(contentScrollView.bounds.size,
YES,
[UIScreen mainScreen].scale);
//this is the key
CGPoint offset=contentScrollView.contentOffset;
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -offset.x, -offset.y);
[contentScrollView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *visibleScrollViewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Cheers :)

Swift version of Abduliam Rehmanius answer.
func screenshot() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.scrollCrop.bounds.size, true, UIScreen.mainScreen().scale);
//this is the key
let offset:CGPoint = self.scrollCrop.contentOffset;
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -offset.x, -offset.y);
self.scrollCrop.layer.renderInContext(UIGraphicsGetCurrentContext()!);
let visibleScrollViewImage: UIImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return visibleScrollViewImage;
}
Swift 4 version:
func screenshot() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.scrollCrop.bounds.size, false, UIScreen.main.scale)
let offset = self.scrollCrop.contentOffset
let thisContext = UIGraphicsGetCurrentContext()
thisContext?.translateBy(x: -offset.x, y: -offset.y)
self.scrollCrop.layer.render(in: thisContext!)
let visibleScrollViewImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return visibleScrollViewImage
}

I've found a solution myself - I took screenshot of the whole view and then crop it to the size and position of UIScrollView frame.
-(UIImage *)imageFromCombinedContext:(UIView *)background
{
UIImage *image;
CGSize size = self.view.frame.size;
UIGraphicsBeginImageContext(size);
[background.layer affineTransform];
[self.view.layer.layer renderInContext:UIGraphicsGetCurrentContext()];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRef imgRef = CGImageCreateWithImageInRect([image CGImage],background.frame);
image = [UIImage imageWithCGImage:imref];
CGImageRelease(imref);
return image;
}

Swift 4 version of Abduliam Rehmanius answer as UIScrollView extension with translation, no slow cropping
extension UIScrollView {
var snapshotVisibleArea: UIImage? {
UIGraphicsBeginImageContext(bounds.size)
UIGraphicsGetCurrentContext()?.translateBy(x: -contentOffset.x, y: -contentOffset.y)
layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}

Jeffery Sun has the right answer. Just put your scroll view inside another view. Get container view to render in context. done.
In the code below, cropView contains the scroll view to be captured. The solution is really just that simple.
As I understand the question and why I found this page, the whole content of the scroll view isn't wanted - just the visible portion.
func captureCrop() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.cropView.frame.size, true, 0.0)
self.cropView.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image;
}

#Abduliam Rehmanius's answer has poor performance, since if the UIScrollView contains a large content area, we will draw that entire content area (even outside the visible bounds).
#Concuror's answer has the issue that it will also draw anything that is on top of the UIScrollView.
My solution was to put the UIScrollView inside a UIView called containerView with the same bounds and then render containerView:
containerView.renderInContext(context)

Swift 3.0 :
func captureScreen() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.yourScrollViewName.bounds.size, true, UIScreen.main.scale)
let offset:CGPoint = self.yourScrollViewName.contentOffset;
UIGraphicsGetCurrentContext()!.translateBy(x: -offset.x, y: -offset.y);
self.yourScrollViewName.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
and use it as :
let Image = captureScreen()

Update swift 3+, 4 on #Concuror code
func getImage(fromCombinedContext background: UIView) -> UIImage {
var image: UIImage?
let size: CGSize = view.frame.size
UIGraphicsBeginImageContext(size)
background.layer.affineTransform()
view.layer.render(in: UIGraphicsGetCurrentContext()!)
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let imgRef = image?.cgImage?.cropping(to: background.frame)
image = UIImage(cgImage: imgRef!)
// CGImageRelease(imgRef!) // Removing on Swift - 'CGImageRelease' is unavailable: Core Foundation objects are automatically memory managed
return image ?? UIImage()
}

A lot of the answers use UIGraphicsBeginImageContext (pre iOS 10.0) to create an image, this creates an image missing the P3 colour gamut - reference https://stackoverflow.com/a/41288197/2481602
extension UIScrollView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
inputView?.layer.render(in: rendererContext.cgContext)
layer.render(in: rendererContext.cgContext)
}
}
}
The above will result in a better quality image being produced.
The second image is clearer, and showing more of the colours - this was done with the UIGraphicsImageRenderer rather than the UIGraphicsBeginImageContext (first Image)

Related

UILabel on UIImage and save that image in gallery in Swift 3 for Photo Editing App

I want to add Label on UIImage, for this I have used One Label And adding this on Image Using imgImage.addSubview(txtLabel).Now I want to Save That Image in Galley. For Saving the Image I have Used below method.
#IBAction func btnSave(_ sender: AnyObject) {
UIImageWriteToSavedPhotosAlbum(imgImage.image!, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
But It will not saving image with Label.
How can I achieve it.
Please Help
Thanks!
you can render image layer with text
like this // imageWithLabel handle final image
txtLabel.backgroundColor = UIColor.clearColor()
txtLabel.textAlignment = .Center
txtLabel.textColor = UIColor.whiteColor()
txtLabel.text = text
UIGraphicsBeginImageContextWithOptions(txtLabel.bounds.size, false, 0);
imgImage.layer.renderInContext(UIGraphicsGetCurrentContext()!)
label.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let imageWithLabel = UIGraphicsGetImageFromCurrentImageContext() // here is final image
UIGraphicsEndImageContext();
You can't use addsubview there You need to draw it. Here is Sample code.
UIGraphicsBeginImageContext(img1.size);
//draw image1
[img1 drawInRect:CGRectMake(0, 0, img1.size.width, img1.size.height)];
//draw label
[label drawTextInRect:CGRectMake((img1.size.width - label.frame.size.width)/2, (img1.size.height - label.frame.size.height)/2, label.frame.size.width, label.frame.size.height)];
//get the final image
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Try:
extension UIImageView {
func makeImage() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
This basically takes a snapshot from your UIImageView.

screenshot is not in proper size IOS

In my IOS App Code I am taking screenshot of UIimageView but when I take it it's not taken properly by following code.
func captureView() -> UIImage {
// let rect: CGRect = self.imageView.bounds
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, 0.0)//add this line
let context: CGContextRef = UIGraphicsGetCurrentContext()!
self.view.layer.renderInContext(context)
let img: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
I want the exect screen shot of the image in imageView because there are more images on it.
Kindly suggest me proper code for getting exact UIImageView Screenshot
How about you add UIScreen.mainScreen().scale instead of 0.0 in UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
func captureView() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
drawViewHierarchyInRect(bounds, afterScreenUpdates: true)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}

Taking Screenshot of UIImageView I need only image IOS

In my IOS App I am taking screenshot of UIImageView. It's very perfect as shown in attachment Photo. But here, I have taken UIImageView content mode aspect fit . I've used this code.
func captureView() -> UIImage {
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false,UIScreen.mainScreen().scale)//add this line
let context: CGContextRef = UIGraphicsGetCurrentContext()!
self.backView.layer.renderInContext(context)
let img: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
Simply I need the snapshot of Image, Not the ImageView. Help me into this.
Try this code:
func captureView() -> UIImage() {
UIGraphicsBeginImageContext(YourimageView.frame.size)
YourimageView.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil)
return img
}

Render an view into an image

I'm using the camera to take pictures and when i press a certain button the createImage method will be called. In here i'm trying to render an view called containerView into the image taken by the camera in previewImageView. However the issue is that the image in the camera is the size 1080x1920 and the containerView is a different size depending on the device. For instance on a iphone 6 it will be 375x503. How can i make sure that these two is rendered proberly together like in the below quick illustration?
in my code the containerView is very small in the bottom corner.
My Code at the moment
func createImage() -> UIImage {
//Get the taken image
let fullSizeImage = previewImageView.image
let newOverLayHeight = fullSizeImage!.size.width / self.containerView!.frame.width * self.containerView!.frame.height
print(newOverLayHeight)
//Render containerView to image
UIGraphicsBeginImageContext(CGSizeMake(fullSizeImage!.size.width, newOverLayHeight));
self.containerView!.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let overlayImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//Merge these two images
let newSize = CGSizeMake(fullSizeImage!.size.width, fullSizeImage!.size.height)
UIGraphicsBeginImageContext( newSize )
fullSizeImage!.drawInRect(CGRectMake(0,0,newSize.width,newSize.height))
//draw overlayImage on top of fullsizeImage
overlayImage.drawInRect(CGRectMake(0,fullSizeImage!.size.height - newOverLayHeight, overlayImage!.size.width,overlayImage.size.height), blendMode:CGBlendMode.Normal, alpha:1.0)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
return newImage
}
What i want

Screenshot Only Part of Screen - Swift

I'm using this Swift code to take a screenshot of my app:
UIGraphicsBeginImageContextWithOptions(UIScreen.mainScreen().bounds.size, false, 0);
self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
How would I go about taking a screenshot of only part of the screen, and not all of it, as I do here?
For example if you want to take the screen shot of the UIView rectangle at position (50,50) with height of (100,150).
First set the Image Context to the size of the rectangle. This sets the image size.
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,150), false, 0);
Now draw the view in this context in such a way that, the visible part of the screenshot starts at (50,50)
self.view.drawViewHierarchyInRect(CGRectMake(-50,-50,view.bounds.size.width,view.bounds.size.height) afterScreenUpdates: true)
This clips the screen shot at (100,150) because we set the size of our context. Now take the UIImage from this.
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
I think this is the best Answer to take a shot of part of the screen:
func screenShot() {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.frame.size.width*0.99,self.frame.size.height*0.70), false, 0)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
self.view?.drawViewHierarchyInRect(CGRectMake(-01, -01, self.frame.size.width, self.frame.size.height), afterScreenUpdates: true)
var screenShot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
for taking a snapshot selecting by area of another UIView
// viewObject: UIView linked from storyboard
var bounds= CGRect(x: -viewObject.frame.minX,y: -viewObject.frame.minY,width: view.bounds.size.width,height: view.bounds.size.height)
UIGraphicsBeginImageContext(viewObject.frame.size)
view.drawHierarchy(in: bounds, afterScreenUpdates: true)
let outputImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
func captureView(view: UIView) -> UIImage {
var screenRect: CGRect = view.bounds
UIGraphicsBeginImageContext(screenRect.size)
UIGraphicsBeginImageContextWithOptions(screenRect.size, true, 0)
var ctx: CGContext = UIGraphicsGetCurrentContext()!
UIColor.black.set()
ctx.fill(screenRect)
view.layer.render(in: ctx)
var newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}

Resources