可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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;
}
回答1:
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 :)
回答2:
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
}
回答3:
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;
}
回答4:
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;
}
回答5:
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
}
}
回答6:
@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)
回答7:
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()
回答8:
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()
}