如何在不损失视网膜显示质量的情况下将UIView捕获到UIImage

Dan*_*iel 293 image-capture scale uikit uiimage retina-display

我的代码适用于普通设备,但在视网膜设备上会产生模糊图像.

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

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

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return img;
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*mmy 655

从使用切换UIGraphicsBeginImageContextUIGraphicsBeginImageContextWithOptions(如本页所述).传递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;
}
Run Code Online (Sandbox Code Playgroud)

在快速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
}
Run Code Online (Sandbox Code Playgroud)

  • @Adam Carter`scale:应用于位图的比例因子.如果指定值0.0,则比例因子将设置为设备主屏幕的比例因子.它已明确记录,因此0.0f在我看来更简单,更好. (18认同)
  • 而不是使用'0.0f`作为scale参数,使用`[[UIScreen mainScreen] scale]`更可接受,它也可以. (6认同)
  • Tommy回答很好,但你仍然需要导入`#import <QuartzCore/QuartzCore.h>`来删除`renderInContext:`警告. (6认同)
  • 对于iOS 7,这个答案已经过时了.请参阅我的答案,了解新的"最佳"方法. (5认同)
  • @WayneLiu你可以明确指定比例因子为2.0,但你可能不会得到完全符合iPhone 4质量的图像,因为任何加载的位图都是标准版本而不是@ 2x版本.我不认为有一个解决方案,因为没有办法指示当前持有"UIImage"的每个人重新加载但强制使用高分辨率版本(并且由于内存较小,你很可能会遇到问题无论如何都可以在视网膜前设备中使用). (2认同)

Dim*_*ima 213

目前接受的答案现已过时,至少如果您支持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;
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特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()
}
Run Code Online (Sandbox Code Playgroud)

根据这篇文章,你可以看到新的iOS7方法drawViewHierarchyInRect:afterScreenUpdates:比它快很多倍renderInContext:.基准

  • 出于某些原因,当我尝试为Google地图标记创建自定义图标时,这对我不起作用; 我得到的只是黑色矩形:( (9认同)
  • 设置afterScreenUpdates为YES为我修复了黑色矩形问题 (7认同)
  • @CarlosP你试过将`afterScreenUpdates:`设置为`YES`吗? (6认同)
  • 毫无疑问,`drawViewHierarchyInRect:afterScreenUpdates:`要快得多.我刚在仪器上运行了一个Time Profiler.我的图像生成从138ms到27ms. (5认同)
  • 我也收到了黑色图片.事实证明,如果我在`viewDidLoad`或`viewWillAppear:`中调用代码,那么图像是黑色的; 我必须在`viewDidAppear:`中这样做.所以我最终还是回到了`renderInContext:`. (4认同)
  • 你先生刚救了我的命和我的应用程序 (2认同)
  • 我也得到了“黑色方块”。我在我的代码中将 'isOpaque' 属性设置为 false,然后它对我有用。 (2认同)

Heb*_*ida 32

我已经基于@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
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑: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()
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))  
let image = UIImage.imageWithView(view)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的想法!另外,您也可以在开始上下文后立即推迟{UIGraphicsEndImageContext()}并避免引入局部变量img;) (2认同)

Glo*_*ogo 24

要通过@Tommy和@Dima改进答案,请使用以下类别将UIView渲染为具有透明背景且不损失质量的UIImage .在iOS7上工作.(或者只是在实现中重用该方法,用self图像替换参考)

UIView的+ RenderViewToImage.h

#import <UIKit/UIKit.h>

@interface UIView (RenderToImage)

- (UIImage *)imageByRenderingView;

@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


Ale*_*kiy 24

iOS版 迅速

使用现代UIGraphicsImageRenderer

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Meh*_*deh 15

斯威夫特3

使用UIView扩展的Swift 3解决方案(基于Dima的答案)应如下所示:

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Les*_*win 6

Drop-in 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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


A.G*_*A.G 5

Swift 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
   }

}
Run Code Online (Sandbox Code Playgroud)

用法:

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)

 }
Run Code Online (Sandbox Code Playgroud)


And*_* M. 5

对于 Swift 5.1,您可以使用此扩展:

extension UIView {

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

        return renderer.image {
            rendererContext in

            layer.render(in: rendererContext.cgContext)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)