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 scrol
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 :)
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;
}
@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)
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()
}
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;
}
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
}