获取UIScrollView的屏幕截图,包括屏幕外的部分
我有一个UIScrollView
派生实现了一个takeScreenshot方法,看起来像这样:
-(void)takeScreenshot { CGRect contextRect = CGRectMake(0, 0, 768, 1004); UIGraphicsBeginImageContext(contextRect.size); [self.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // do something with the viewImage here. }
这基本上移动到滚动视图的顶部,并截取可见区域。 当iPad面向人像时,它可以正常工作,但是当它处于横向时,图像的底部被切断(因为可见区域的高度仅为748,而不是1004)。
是否有可能获得UIScrollView
的快照,包括不在屏幕上的区域? 或者我需要向下滚动视图,拍摄第二张照片并将它们拼接在一起?
这是代码工作…
- (IBAction) renderScrollViewToImage { UIImage* image = nil; UIGraphicsBeginImageContext(_scrollView.contentSize); { CGPoint savedContentOffset = _scrollView.contentOffset; CGRect savedFrame = _scrollView.frame; _scrollView.contentOffset = CGPointZero; _scrollView.frame = CGRectMake(0, 0, _scrollView.contentSize.width, _scrollView.contentSize.height); [_scrollView.layer renderInContext: UIGraphicsGetCurrentContext()]; image = UIGraphicsGetImageFromCurrentImageContext(); _scrollView.contentOffset = savedContentOffset; _scrollView.frame = savedFrame; } UIGraphicsEndImageContext(); if (image != nil) { [UIImagePNGRepresentation(image) writeToFile: @"/tmp/test.png" atomically: YES]; system("open /tmp/test.png"); } }
最后几行只是将图像写入/tmp/test.png,然后在Preview.app中打开它。 这显然只适用于模拟器:-)
在ScrollViewScreenShot Github Repository中完成项目
为我回答https://stackoverflow.com/a/3539944/4164443没有工作。; 我有一个在iOS 8上实现这个任务。
其实这种方法适用于iPhone,但iPad(模拟和真实设备)是另一种情况。 它只是呈现可见的部分,其余的图像只是空白。
我试着用drawViewHierarchyInRect
– 没有运气。 根据afterScreenUpdates
是true
或false
我得到了图像的延伸部分或再次只是图像的一部分。
所以我发现实现正确屏幕截图的唯一方法是将scrollview添加到另一个临时视图并呈现它。
示例代码如下(我的VC中scrollview是出口)
func getImageOfScrollView()->UIImage{ var image = UIImage(); UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.mainScreen().scale) // save initial values let savedContentOffset = self.scrollView.contentOffset; let savedFrame = self.scrollView.frame; let savedBackgroundColor = self.scrollView.backgroundColor // reset offset to top left point self.scrollView.contentOffset = CGPointZero; // set frame to content size self.scrollView.frame = CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height); // remove background self.scrollView.backgroundColor = UIColor.clearColor() // make temp view with scroll view content size // a workaround for issue when image on ipad was drawn incorrectly let tempView = UIView(frame: CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height)) // save superview let tempSuperView = self.scrollView.superview // remove scrollView from old superview self.scrollView.removeFromSuperview() // and add to tempView tempView.addSubview(self.scrollView) // render view // drawViewHierarchyInRect not working correctly tempView.layer.renderInContext(UIGraphicsGetCurrentContext()) // and get image image = UIGraphicsGetImageFromCurrentImageContext(); // and return everything back tempView.subviews[0].removeFromSuperview() tempSuperView?.addSubview(self.scrollView) // restore saved settings self.scrollView.contentOffset = savedContentOffset; self.scrollView.frame = savedFrame; self.scrollView.backgroundColor = savedBackgroundColor UIGraphicsEndImageContext(); return image }
使用UIScrollView处理UIView扩展的工作示例:
extension UIView { func screenshot() -> UIImage { if(self is UIScrollView) { let scrollView = self as! UIScrollView let savedContentOffset = scrollView.contentOffset let savedFrame = scrollView.frame UIGraphicsBeginImageContext(scrollView.contentSize) scrollView.contentOffset = .zero self.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height) self.layer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext(); scrollView.contentOffset = savedContentOffset scrollView.frame = savedFrame return image! } UIGraphicsBeginImageContext(self.bounds.size) self.layer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! } }
如果您不想将滚动视图展开到整个屏幕之外(无论如何都不能使用自动布局),还有更好的方法。
您可以将核心graphics转换与滚动视图的contentOffset
结合使用来完成相同的操作。
// // ScrollViewSnapshotter.swift // ScrollViewSnapshotter // // Created by Moshe Berman on 4/10/16. // Copyright © 2016 Moshe Berman. All rights reserved. // import UIKit class ScrollViewSnapshotter: NSObject { func PDFWithScrollView(scrollview: UIScrollView) -> NSData { /** * Step 1: The first thing we need is the default origin and size of our pages. * Since bounds always start at (0, 0) and the scroll view's bounds give us * the correct size for the visible area, we can just use that. * * In the United States, a standard printed page is 8.5 inches by 11 inches, * but when generating a PDF it's simpler to keep the page size matching the * visible area of the scroll view. We can let our printer software (such * as the Preview app on OS X or the Printer app on iOS) do the scaling. * * If we wanted to scale ourselves, we could multiply each of those * numbers by 72, to get the number of points for each dimension. * We would have to change how we generated the the pages below, so * for simplicity, we're going to stick to one page per screenful of content. */ let pageDimensions = scrollview.bounds /** * Step 2: Now we need to know how many pages we will need to fit our content. * To get this, we divide our scroll views dimensions by the size * of each page, in either direction. * We also need to round up, so that the pages don't get clipped. */ let pageSize = pageDimensions.size let totalSize = scrollview.contentSize let numberOfPagesThatFitHorizontally = Int(ceil(totalSize.width / pageSize.width)) let numberOfPagesThatFitVertically = Int(ceil(totalSize.height / pageSize.height)) /** * Step 3: Set up a Core Graphics PDF context. * * First we create a backing store for the PDF data, then * pass it and the page dimensions to Core Graphics. * * We could pass in some document information here, which mostly cover PDF metadata, * including author name, creator name (our software) and a password to * require when viewing the PDF file. * * Also note that we can use UIGraphicsBeginPDFContextToFile() instead, * which writes the PDF to a specified path. I haven't played with it, so * I don't know if the data is written all at once, or as each page is closed. */ let outputData = NSMutableData() UIGraphicsBeginPDFContextToData(outputData, pageDimensions, nil) /** * Step 4: Remember some state for later. * Then we need to clear the content insets, so that our * core graphics layer and our content offset match up. * We don't need to reset the content offset, because that * happens implicitly, in the loop below. */ let savedContentOffset = scrollview.contentOffset let savedContentInset = scrollview.contentInset scrollview.contentInset = UIEdgeInsetsZero /** * Step 6: Now we loop through the pages and generate the data for each page. */ if let context = UIGraphicsGetCurrentContext() { for indexHorizontal in 0 ..< numberOfPagesThatFitHorizontally { for indexVertical in 0 ..< numberOfPagesThatFitVertically { /** * Step 6a: Start a new page. * * This automatically closes the previous page. * There's a similar method UIGraphicsBeginPDFPageWithInfo, * which allows you to configure the rectangle of the page and * other metadata. */ UIGraphicsBeginPDFPage() /** * Step 6b:The trick here is to move the visible portion of the * scroll view *and* adjust the core graphics context * appropriately. * * Consider that the viewport of the core graphics context * is attached to the top of the scroll view's content view * and we need to push it in the opposite direction as we scroll. * Further, anything not inside of the visible area of the scroll * view is clipped, so scrolling will move the core graphics viewport * out of the rendered area, producing empty pages. * * To counter this, we scroll the next screenful into view, and adjust * the core graphics context. Note that core graphics uses a coordinate * system which has the y coordinate decreasing as we go from top to bottom. * This is the opposite of UIKit (although it matches AppKit on OS X.) */ let offsetHorizontal = CGFloat(indexHorizontal) * pageSize.width let offsetVertical = CGFloat(indexVertical) * pageSize.height scrollview.contentOffset = CGPointMake(offsetHorizontal, offsetVertical) CGContextTranslateCTM(context, -offsetHorizontal, -offsetVertical) // NOTE: Negative offsets /** * Step 6c: Now we are ready to render the page. * * There are faster ways to snapshot a view, but this * is the most straightforward way to render a layer * into a context. */ scrollview.layer.renderInContext(context) } } } /** * Step 7: End the document context. */ UIGraphicsEndPDFContext() /** * Step 8: Restore the scroll view. */ scrollview.contentInset = savedContentInset scrollview.contentOffset = savedContentOffset /** * Step 9: Return the data. * You can write it to a file, or display it the user, * or even pass it to iOS for sharing. */ return outputData } }
这是我写的一篇博客文章,解释了这个过程。
生成PDF的过程与快照图像非常相似,除了页面之外,您需要制作一个与滚动视图大小相匹配的大型canvas,然后以大块forms抓取内容。
我不知道多less,但我可以猜测,如果我们设置contextRect
像这样的大小的景观,它可能会运作良好:
CGRect contextRect = CGRectMake(0, 0, 1004, 768*2);
因为这个contextRect将决定UIGraphicsBeginImageContext
的大小,所以我希望高度的两倍可以解决你的问题
这是另一种方法,它将缩放级别考虑在内。 我有一个滚动视图,其中有4个不同的UIImageView图层,我想截取他们当前状态的截图:
float theScale = 1.0f / theScrollView.zoomScale; // The viewing rectangle in absolute coordinates CGRect visibleArea = CGRectMake((int)(theScrollView.contentOffset.x * theScale), (int)(theScrollView.contentOffset.y * theScale), (int)(theScrollView.bounds.size.width * theScale), (int)(theScrollView.bounds.size.height * theScale)); NSArray *layers = [NSArray arrayWithObjects:imageLayer1, imageLayer2, imageLayer3, imageLayer4, nil]; UIGraphicsBeginImageContext(visibleArea.size); for (UIImageView *layer in layers) { CALayer *coreLayer = layer.layer; coreLayer.bounds = CGRectMake(layer.frame.origin.x - visibleArea.origin.x, layer.frame.origin.y - visibleArea.origin.y, layer.frame.size.width, layer.frame.size.height); [coreLayer renderInContext:UIGraphicsGetCurrentContext()]; } UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
这需要绝对坐标的截图。 也就是说,如果在滚动视图中有一个2048 * 2048的图像,并且您可以看到它的四分之一,那么不pipe屏幕的分辨率如何,它都会有512 * 512的屏幕截图。 如果你想在你的屏幕分辨率(比如说320 * 480)上截图,那么你必须按如下方式调整图像,直接在上面的代码之后:
UIGraphicsBeginImageContext(theScrollView.frame.size); [screenshot drawInRect:CGRectMake(0, 0, theScrollView.frame.size.width, theScrollView.frame.size.height)]; UIImage *smallScreenshot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
SWIFT 3版本感谢@gleb vodovozov:
func getImageOfScrollView()->UIImage{ var image = UIImage(); UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.main.scale) // save initial values let savedContentOffset = self.scrollView.contentOffset; let savedFrame = self.scrollView.frame; let savedBackgroundColor = self.scrollView.backgroundColor // reset offset to top left point self.scrollView.contentOffset = CGPoint.zero; // set frame to content size self.scrollView.frame = CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height) // remove background self.scrollView.backgroundColor = UIColor.clear // make temp view with scroll view content size // a workaround for issue when image on ipad was drawn incorrectly let tempView = UIView(frame: CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height)) // save superview let tempSuperView = self.scrollView.superview // remove scrollView from old superview self.scrollView.removeFromSuperview() // and add to tempView tempView.addSubview(self.scrollView) // render view // drawViewHierarchyInRect not working correctly tempView.layer.render(in: UIGraphicsGetCurrentContext()!) // and get image image = UIGraphicsGetImageFromCurrentImageContext()!; // and return everything back tempView.subviews[0].removeFromSuperview() tempSuperView?.addSubview(self.scrollView) // restore saved settings self.scrollView.contentOffset = savedContentOffset; self.scrollView.frame = savedFrame; self.scrollView.backgroundColor = savedBackgroundColor UIGraphicsEndImageContext(); return image }