如何捕获UIView到UIImage而不损失视网膜显示的质量

我的代码在普通设备上工作正常,但在视网膜设备上产生模糊的图像。

有人知道我的问题的解决方案吗?

+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContext(view.bounds.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];


UIImage * img = UIGraphicsGetImageFromCurrentImageContext();


UIGraphicsEndImageContext();


return img;
}
165876 次浏览

从使用UIGraphicsBeginImageContext切换到使用UIGraphicsBeginImageContextWithOptions(如文档中的在本页)。通过0.0进行缩放(第三个参数),您将获得一个缩放因子与屏幕的缩放因子相等的上下文。

UIGraphicsBeginImageContext使用了1.0的固定缩放因子,所以你实际上在iPhone 4和其他iPhone上得到了完全相同的图像。我敢打赌,要么是iPhone 4在你隐式放大它的时候应用了一个过滤器,要么就是你的大脑发现它比周围的所有东西都更不清晰。

所以,我想:

#import <QuartzCore/QuartzCore.h>


+ (UIImage *)imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];


UIImage * img = UIGraphicsGetImageFromCurrentImageContext();


UIGraphicsEndImageContext();


return img;
}

在Swift 4中:

func image(with view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
defer { UIGraphicsEndImageContext() }
if let context = UIGraphicsGetCurrentContext() {
view.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
return nil
}

将此添加到UIView Category的方法

- (UIImage*) capture {
UIGraphicsBeginImageContext(self.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
有时drawRect方法使问题,所以我得到这些答案更合适。你也可以看看 在DrawRect方法中捕获UIView的UIImage < / p >

目前接受的答案已经过时了,至少如果你支持iOS 7的话。

如果你只支持iOS7+,下面是你应该使用的:

+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0f);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshotImage;
}

斯威夫特4:

func imageWithView(view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
defer { UIGraphicsEndImageContext() }
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
return UIGraphicsGetImageFromCurrentImageContext()
}

根据这篇文章,你可以看到新的iOS7方法drawViewHierarchyInRect:afterScreenUpdates:renderInContext:快很多倍。benchmark

为了改进@Tommy和@Dima的回答,使用以下类别将UIView渲染为UIImage 透明背景并且不损失质量。在iOS7上工作。(或者只是在实现中重用该方法,将self引用替换为您的图像)

UIView + RenderViewToImage.h

#import <UIKit/UIKit.h>


@interface UIView (RenderToImage)


- (UIImage *)imageByRenderingView;


@end

UIView + RenderViewToImage.m

#import "UIView+RenderViewToImage.h"


@implementation UIView (RenderViewToImage)


- (UIImage *)imageByRenderingView
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshotImage;
}


@end

我已经创建了一个基于@Dima解决方案的Swift扩展:

extension UIImage {
class func imageWithView(view: UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
}

编辑:Swift 4改进版本

extension UIImage {
class func imageWithView(_ view: UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0)
defer { UIGraphicsEndImageContext() }
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}
}

用法:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let image = UIImage.imageWithView(view)
- (UIImage*)screenshotForView:(UIView *)view
{
UIGraphicsBeginImageContext(view.bounds.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


// hack, helps w/ our colors when blurring
NSData *imageData = UIImageJPEGRepresentation(image, 1); // convert to jpeg
image = [UIImage imageWithData:imageData];


return image;
}

斯威夫特2.0:

使用扩展方法:

extension UIImage{


class func renderUIViewToImage(viewToBeRendered:UIView?) -> UIImage
{
UIGraphicsBeginImageContextWithOptions((viewToBeRendered?.bounds.size)!, false, 0.0)
viewToBeRendered!.drawViewHierarchyInRect(viewToBeRendered!.bounds, afterScreenUpdates: true)
viewToBeRendered!.layer.renderInContext(UIGraphicsGetCurrentContext()!)


let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()


return finalImage
}


}

用法:

override func viewDidLoad() {
super.viewDidLoad()


//Sample View To Self.view
let sampleView = UIView(frame: CGRectMake(100,100,200,200))
sampleView.backgroundColor =  UIColor(patternImage: UIImage(named: "ic_120x120")!)
self.view.addSubview(sampleView)


//ImageView With Image
let sampleImageView = UIImageView(frame: CGRectMake(100,400,200,200))


//sampleView is rendered to sampleImage
var sampleImage = UIImage.renderUIViewToImage(sampleView)


sampleImageView.image = sampleImage
self.view.addSubview(sampleImageView)


}

在这个方法中,只需要传递一个view对象它就会返回一个UIImage对象。

-(UIImage*)getUIImageFromView:(UIView*)yourView
{
UIGraphicsBeginImageContext(yourView.bounds.size);
[yourView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

斯威夫特3

带有UIView扩展的斯威夫特3解决方案(基于迪玛的回答)应该像这样:

extension UIView {
public func getSnapshotImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
let snapshotImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return snapshotImage
}
}

Swift 3.0实现

extension UIView {
func getSnapshotImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: false)
let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return snapshotImage
}
}
< p > < img src = " https://img.shields.io/badge/iOS-10。* -blue.svg”alt = " iOS " / > < img src = " https://img.shields.io/badge/swift - 3.0。* -orange.svg”alt = "快速" / > < / p >

使用现代的uigraphicsimagerender

public extension UIView {
@available(iOS 10.0, *)
public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage {
let rendererFormat = UIGraphicsImageRendererFormat.default()
rendererFormat.opaque = isOpaque
let renderer = UIGraphicsImageRenderer(size: bounds.size, format: rendererFormat)


let snapshotImage = renderer.image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
}
return snapshotImage
}
}

所有Swift 3的答案都不适合我,所以我翻译了最受欢迎的答案:

extension UIImage {
class func imageWithView(view: UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let img: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
}

Swift 3.0扩展,支持新的iOS 10.0 API &前面的方法。

注意:

  • iOS版本检查
  • 注意使用defer来简化上下文清理。
  • 还将应用不透明度&视图的当前比例。
  • 没有任何东西只是使用!解包装,这可能会导致崩溃。

extension UIView
{
public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage?
{
if #available(iOS 10.0, *)
{
let rendererFormat = UIGraphicsImageRendererFormat.default()
rendererFormat.scale = self.layer.contentsScale
rendererFormat.opaque = self.isOpaque
let renderer = UIGraphicsImageRenderer(size: self.bounds.size, format: rendererFormat)


return
renderer.image
{
_ in


self.drawHierarchy(in: self.bounds, afterScreenUpdates: afterScreenUpdates)
}
}
else
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, self.layer.contentsScale)
defer
{
UIGraphicsEndImageContext()
}


self.drawHierarchy(in: self.bounds, afterScreenUpdates: afterScreenUpdates)


return UIGraphicsGetImageFromCurrentImageContext()
}
}
}

下面是一个基于@Dima的回答的Swift 4__abc0。

extension UIView {
func snapshotImage() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}

UIGraphicsImageRenderer是一个相对较新的API,在iOS 10中引入。你构造一个uigraphicsimagerender 通过指定一个点的大小。image方法接受一个闭包参数,并返回执行传递的闭包所产生的位图。在这种情况下,结果是原始图像按比例缩小以在指定的边界内绘制。

https://nshipster.com/image-resizing/

因此,请确保传递给UIGraphicsImageRenderer的大小是点,而不是像素。

如果图像比预期的要大,则需要将图像大小除以比例因子。

对于Swift 5.1,你可以使用这个扩展:

extension UIView {


func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)


return renderer.image { layer.render(in: $0.cgContext) }
}
}