如何以编程方式使用UIGraphicsImageRenderer拍摄高质量的屏幕截图? [英] How to take high-quality screenshot with UIGraphicsImageRenderer programmatically?

查看:88
本文介绍了如何以编程方式使用UIGraphicsImageRenderer拍摄高质量的屏幕截图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:截图后,通过缩放检查图像模糊.缩放后,图像中的文本似乎模糊.

我知道这个问题已经提出很多次了,但是没有一个人有想要的解决方案.我已经检查了很多帖子,例如

10倍缩放:

该示例的代码:

  class ViewController:UIViewController,UIScrollViewDelegate {让zoomLabel:UILabel = UILabel()让scrollView:UIScrollView = UIScrollView()覆盖func viewDidLoad(){super.viewDidLoad()[zoomLabel,scrollView] .forEach {$ 0.translatesAutoresizingMaskIntoConstraints = false}scrollView.addSubview(zoomLabel)view.addSubview(scrollView)让g = view.safeAreaLayoutGuideNSLayoutConstraint.activate([scrollView.centerXAnchor.constraint(等于:g.centerXAnchor),scrollView.centerYAnchor.constraint(等于:g.centerYAnchor),scrollView.widthAnchor.constraint(equalToConstant:300.0),scrollView.heightAnchor.constraint(equalToConstant:200.0),zoomLabel.topAnchor.constraint(equalTo:scrollView.contentLayoutGuide.topAnchor),zoomLabel.leadingAnchor.constraint(equalTo:scrollView.contentLayoutGuide.leadingAnchor),zoomLabel.trailingAnchor.constraint(equalTo:scrollView.contentLayoutGuide.trailingAnchor),zoomLabel.bottomAnchor.constraint(equalTo:scrollView.contentLayoutGuide.bottomAnchor),])zoomLabel.textColor = .redzoomLabel.backgroundColor = .yellowzoomLabel.font = UIFont.systemFont(ofSize:30.0,weight:.regular)zoomLabel.text =示例文本";scrollView.delegate =自我scrollView.minimumZoomScale = 1scrollView.maximumZoomScale = 10view.backgroundColor = UIColor(白色:0.9,Alpha:1.0)scrollView.backgroundColor = .white}func viewForZooming(在scrollView:UIScrollView中)->UIView?{返回zoomLabel}} 

当您捕获视图内容"时,作为 UIImage ,您将获得一个位图,它是视图的大小(以磅为单位) x屏幕比例.

因此,例如,在iPhone 8上,在屏幕尺寸为 @ 2x 的情况下,以 300 x 200 视图将被捕获".作为具有 600 x 400 像素的 UIImage .

无论是缩放视图本身还是视图的位图捕获,都将得到相同的结果-缩放时边缘模糊.

您的评论包括:……编辑图像时……" -这是一个常见问题,我们希望允许用户添加文本(标签),Bezier路径形状,附加图像等.例如,用户在屏幕上看到的可能是 3000 x 2000 像素的原始图像,显示在 300 x 200 点.在屏幕上 添加一个30点标签看起来不错,但随后将其作为 UIImage (用于缩放或保存到磁盘)进行抓取,最终得到为 600 x 400 像素的图像,当然,在较大尺寸的图像上看起来效果不佳.

通常,解决此问题的方法如下:

允许用户按屏幕尺寸进行编辑,例如

  • 300 x 200 视图中显示按比例缩小的 3000 x 2000 像素图像
  • 添加直角椭圆形的贝塞尔曲线(20,20,200,200)
  • 在原点(32,32)
  • 处添加30点标签

然后,当捕获"时,用于输出/缩放的

  • 拍摄原始 3000 x 2000 像素图像
  • 添加矩形的贝塞尔曲线(20 * 10、20 * 10、200 * 10、200 * 10)
  • 在原点(32 * 10,32 * 10)
  • 处添加(30 * 10)点标签

另一种选择是进行屏幕编辑 缩小 .

因此,您可以使用 300 x 200 图像视图,以及 3000 x 2000 像素图像(缩放以适合).当用户说我想在rect (20,20,200,200)中添加一个椭圆形贝塞尔曲线路径时,您的代码会在rect (20 * 10,20* 10、200 * 10、200 * 10) 图像本身,然后刷新图像视图的 .image 属性.

下面是一个更详细的示例,以帮助您弄清楚一切:

  class ViewController:UIViewController,UIScrollViewDelegate {让topView:UIView = UIView()让topLabel:UILabel = UILabel()让botView:UIView = UIView()让botLabel:UILabel = UILabel()让topScrollView:UIScrollView = UIScrollView()让botScrollView:UIScrollView = UIScrollView()让topStatLabel:UILabel = UILabel()让botStatLabel:UILabel = UILabel()覆盖func viewDidLoad(){super.viewDidLoad()[topView,topLabel,botView,botLabel,topScrollView,botScrollView,topStatLabel,botStatLabel] .forEach {$ 0.translatesAutoresizingMaskIntoConstraints = false}topView.addSubview(topLabel)botView.addSubview(botLabel)topScrollView.addSubview(topView)botScrollView.addSubview(botView)view.addSubview(topStatLabel)view.addSubview(botStatLabel)view.addSubview(topScrollView)view.addSubview(botScrollView)让g = view.safeAreaLayoutGuideNSLayoutConstraint.activate([topStatLabel.topAnchor.constraint(等于:g.topAnchor,常数:20.0),topStatLabel.leadingAnchor.constraint(equalTo:topScrollView.leadingAnchor),topScrollView.topAnchor.constraint(等于:topStatLabel.bottomAnchor,常数:4.0),topScrollView.centerXAnchor.constraint(等于:g.centerXAnchor),topScrollView.widthAnchor.constraint(equalToConstant:300.0),topScrollView.heightAnchor.constraint(equalToConstant:200.0),botScrollView.topAnchor.constraint(等于:topScrollView.bottomAnchor,常数:12.0),botScrollView.centerXAnchor.constraint(等于:g.centerXAnchor),botScrollView.widthAnchor.constraint(equalToConstant:300.0),botScrollView.heightAnchor.constraint(equalToConstant:200.0),botStatLabel.topAnchor.constraint(等于:botScrollView.bottomAnchor,常数:4.0),botStatLabel.leadingAnchor.constraint(等于:botScrollView.leadingAnchor),topView.widthAnchor.constraint(equalToConstant:300.0),topView.heightAnchor.constraint(equalToConstant:200.0),botView.widthAnchor.constraint(equalToConstant:300.0 * 10.0),botView.heightAnchor.constraint(equalToConstant:200.0 * 10.0),topLabel.topAnchor.constraint(equalTo:topView.topAnchor,常数:8.0),topLabel.leadingAnchor.constraint(equalTo:topView.leadingAnchor,常数:8.0),botLabel.topAnchor.constraint(等于:botView.topAnchor,常数:8.0 * 10.0),botLabel.leadingAnchor.constraint(等于:botView.leadingAnchor,常数:8.0 * 10.0),topView.topAnchor.constraint(equalTo:topScrollView.contentLayoutGuide.topAnchor),topView.leadingAnchor.constraint(equalTo:topScrollView.contentLayoutGuide.leadingAnchor),topView.trailingAnchor.constraint(equalTo:topScrollView.contentLayoutGuide.trailingAnchor),topView.bottomAnchor.constraint(equalTo:topScrollView.contentLayoutGuide.bottomAnchor),botView.topAnchor.constraint(等于:botScrollView.contentLayoutGuide.topAnchor),botView.leadingAnchor.constraint(等于:botScrollView.contentLayoutGuide.leadingAnchor),botView.trailingAnchor.constraint(等于:botScrollView.contentLayoutGuide.trailingAnchor),botView.bottomAnchor.constraint(等于:botScrollView.contentLayoutGuide.bottomAnchor),])topLabel.textColor = .redtopLabel.backgroundColor = .yellowtopLabel.font = UIFont.systemFont(ofSize:30.0,weight:.regular)topLabel.text =示例文本";botLabel.textColor = .redbotLabel.backgroundColor = .yellowbotLabel.font = UIFont.systemFont(ofSize:30.0 * 10.0,weight:.regular)botLabel.text =示例文本";topScrollView.delegate =自我topScrollView.minimumZoomScale = 1topScrollView.maximumZoomScale = 10botScrollView.delegate =自我botScrollView.minimumZoomScale = 0.1botScrollView.maximumZoomScale = 1topScrollView.zoomScale = topScrollView.minimumZoomScalebotScrollView.zoomScale = botScrollView.minimumZoomScaleview.backgroundColor = UIColor(白色:0.9,Alpha:1.0)topScrollView.backgroundColor = .whitebotScrollView.backgroundColor = .whitetopStatLabel.font = UIFont.systemFont(ofSize:14,重量:.light)topStatLabel.numberOfLines = 0botStatLabel.font = UIFont.systemFont(ofSize:14,重量:.light)botStatLabel.numberOfLines = 0令t = UITapGestureRecognizer(目标:自我,行动​​:#selector(self.tapped(_ :)))view.addGestureRecognizer(t)}覆盖func viewDidAppear(_动画:布尔){super.viewDidAppear(动画)updateStatLabels()}func updateStatLabels()->空白 {var sTop ="sTop + =标签点大小:\(topLabel.font.pointSize)"sTop + ="\ n"sTop + =标签框架:\(topLabel.frame)"sTop + ="\ n"sTop + =视图大小:\(topView.bounds.size)"sTop + ="\ n"sTop + =缩放比例:\(String(格式:%0.1f",topScrollView.zoomScale))""var sBot ="sBot + =缩放比例:\(String(格式:%0.1f",botScrollView.zoomScale))"sBot + ="\ n"sBot + =视图大小:\(botView.bounds.size)"sBot + ="\ n"sBot + =标签框架:\(botLabel.frame)";sBot + ="\ n"sBot + =标签点大小:\(botLabel.font.pointSize)"topStatLabel.text = sTopbotStatLabel.text = sBot}func viewForZooming(在scrollView:UIScrollView中)->UIView?{如果scrollView == topScrollView {返回topView}返回botView}使用@objc func(_ g:UITapGestureRecognizer)->空白 {如果Int(topScrollView.zoomScale)== Int(topScrollView.maximumZoomScale){topScrollView.zoomScale = topScrollView.minimumZoomScale} 别的 {topScrollView.zoomScale + = 1}topScrollView.contentOffset = .zero//直接比较浮点数将失败,因此将值取整如果round(botScrollView.zoomScale * 10)== round(botScrollView.maximumZoomScale * 10){botScrollView.zoomScale = botScrollView.minimumZoomScale} 别的 {botScrollView.zoomScale + = 0.1}botScrollView.contentOffset = .zeroupdateStatLabels()}} 

顶部滚动视图具有带有30点标签的 300 x 200 视图,允许zoomScale从 1扩展到10 .

底部滚动视图具有带有300点标签的 3000 x 2000 视图,允许zoomScale从 0.1到1.0 .

每次点击屏幕时,scrollViews分别将zoomScale增大1和0.1.

它看起来像这样:

5级和0.5级:

以及10和1.0比例:

PROBLEM: After I take screenshot the image is blurry when check by zooming. The text inside image seems to be blurred when zoomed.

I know this question have been raised many a times but none of them have desired solution. I already checked quite a few post like this one

All the solution been shared so far on this forum are repeated or same in any other way but none of them has a solution for the problem.

Here is what I am doing:

extension UIView {

  func asImage() -> UIImage? {
    let format = UIGraphicsImageRendererFormat()
    format.opaque = self.isOpaque
    let renderer = UIGraphicsImageRenderer(bounds: bounds,format: format)
    return renderer.image(actions: { rendererContext in
        layer.render(in: rendererContext.cgContext)
    })
}

//The other option using UIGraphicsEndImageContext

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

The above function will convert UIView into and image but the image quality returned is not up-to the mark.

解决方案

You won't get your desired results by doing a UIView "image capture."

When you zoom a UIScrollView it does not perform a vector scaling... it performs a rasterized scaling.

You can easily confirm this by using a UILabel as the viewForZooming. Here is a label with 30-point system font...

at 1x zoom:

at 10x zoom:

Code for that example:

class ViewController: UIViewController, UIScrollViewDelegate {
    
    let zoomLabel: UILabel = UILabel()
    let scrollView: UIScrollView = UIScrollView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        [zoomLabel, scrollView].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
        }
        
        scrollView.addSubview(zoomLabel)
        view.addSubview(scrollView)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            scrollView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            scrollView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
            scrollView.widthAnchor.constraint(equalToConstant: 300.0),
            scrollView.heightAnchor.constraint(equalToConstant: 200.0),
            
            zoomLabel.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
            zoomLabel.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
            zoomLabel.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
            zoomLabel.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
            
        ])
        
        zoomLabel.textColor = .red
        zoomLabel.backgroundColor = .yellow
        zoomLabel.font = UIFont.systemFont(ofSize: 30.0, weight: .regular)
        zoomLabel.text = "Sample Text"
        
        scrollView.delegate = self
        scrollView.minimumZoomScale = 1
        scrollView.maximumZoomScale = 10
        
        view.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        scrollView.backgroundColor = .white
    }
    

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return zoomLabel
    }

}

When you "capture the view content" as a UIImage, you get a bitmap that is the size of the view in points x the screen scale.

So, on an iPhone 8, for example, with @2x screen scale, at 300 x 200 view will be "captured" as a UIImage with 600 x 400 pixels.

Whether you zoom the view itself, or a bitmap-capture of the view, you'll get the same result -- blurry edges when zoomed.

Your comments include: "... while editing image ..." -- this is a common issue, where we want to allow the user to add text (labels), Bezier Path shapes, addition images, etc. What the user sees on the screen, for example, may be an original image of 3000 x 2000 pixels, displayed at 300 x 200 points. Adding a 30-point label might look good on the screen, but then grabbing that as a UIImage (either for zooming or for saving to disk), ends up as a 600 x 400 pixel image which, of course, will not look good at a larger size.

Frequently, the approach to resolve this is along these lines:

Allow the user to edit at screen dimensions, e.g.

  • show a 3000 x 2000 pixel image scaled down in a 300 x 200 view
  • add a Bezier Path, oval-in-rect (20, 20, 200, 200)
  • add a 30-point label at origin (32, 32)

Then, when "capturing" that for output / zooming

  • take the original 3000 x 2000 pixel image
  • add a Bezier Path, oval-in-rect (20 * 10, 20 * 10, 200 * 10, 200 * 10)
  • add a (30 * 10)-point label at origin (32 * 10, 32 * 10)

Another option is to do the on-screen editing scaled-down.

So, you might use a 300 x 200 image view, with your 3000 x 2000 pixel image (scale to fit). When the user says "I want to add an oval Bezier Path in rect (20, 20, 200, 200), your code would draw that oval at rect (20 * 10, 20 * 10, 200 * 10, 200 * 10) on the image itself and then refresh the .image property of the image view.

Here's a little more detailed example to help make things clear:

class ViewController: UIViewController, UIScrollViewDelegate {
    
    let topView: UIView = UIView()
    let topLabel: UILabel = UILabel()
    
    let botView: UIView = UIView()
    let botLabel: UILabel = UILabel()
    
    let topScrollView: UIScrollView = UIScrollView()
    let botScrollView: UIScrollView = UIScrollView()

    let topStatLabel: UILabel = UILabel()
    let botStatLabel: UILabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        [topView, topLabel, botView, botLabel, topScrollView, botScrollView, topStatLabel, botStatLabel].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
        }
        
        topView.addSubview(topLabel)
        botView.addSubview(botLabel)

        topScrollView.addSubview(topView)
        botScrollView.addSubview(botView)

        view.addSubview(topStatLabel)
        view.addSubview(botStatLabel)
        
        view.addSubview(topScrollView)
        view.addSubview(botScrollView)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            topStatLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            topStatLabel.leadingAnchor.constraint(equalTo: topScrollView.leadingAnchor),

            topScrollView.topAnchor.constraint(equalTo: topStatLabel.bottomAnchor, constant: 4.0),
            topScrollView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            topScrollView.widthAnchor.constraint(equalToConstant: 300.0),
            topScrollView.heightAnchor.constraint(equalToConstant: 200.0),
            
            botScrollView.topAnchor.constraint(equalTo: topScrollView.bottomAnchor, constant: 12.0),
            botScrollView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            botScrollView.widthAnchor.constraint(equalToConstant: 300.0),
            botScrollView.heightAnchor.constraint(equalToConstant: 200.0),
            
            botStatLabel.topAnchor.constraint(equalTo: botScrollView.bottomAnchor, constant: 4.0),
            botStatLabel.leadingAnchor.constraint(equalTo: botScrollView.leadingAnchor),
            
            topView.widthAnchor.constraint(equalToConstant: 300.0),
            topView.heightAnchor.constraint(equalToConstant: 200.0),
            
            botView.widthAnchor.constraint(equalToConstant: 300.0 * 10.0),
            botView.heightAnchor.constraint(equalToConstant: 200.0 * 10.0),
            
            topLabel.topAnchor.constraint(equalTo: topView.topAnchor, constant: 8.0),
            topLabel.leadingAnchor.constraint(equalTo: topView.leadingAnchor, constant: 8.0),

            botLabel.topAnchor.constraint(equalTo: botView.topAnchor, constant: 8.0 * 10.0),
            botLabel.leadingAnchor.constraint(equalTo: botView.leadingAnchor, constant: 8.0 * 10.0),
            
            topView.topAnchor.constraint(equalTo: topScrollView.contentLayoutGuide.topAnchor),
            topView.leadingAnchor.constraint(equalTo: topScrollView.contentLayoutGuide.leadingAnchor),
            topView.trailingAnchor.constraint(equalTo: topScrollView.contentLayoutGuide.trailingAnchor),
            topView.bottomAnchor.constraint(equalTo: topScrollView.contentLayoutGuide.bottomAnchor),

            botView.topAnchor.constraint(equalTo: botScrollView.contentLayoutGuide.topAnchor),
            botView.leadingAnchor.constraint(equalTo: botScrollView.contentLayoutGuide.leadingAnchor),
            botView.trailingAnchor.constraint(equalTo: botScrollView.contentLayoutGuide.trailingAnchor),
            botView.bottomAnchor.constraint(equalTo: botScrollView.contentLayoutGuide.bottomAnchor),
            
        ])
        
        topLabel.textColor = .red
        topLabel.backgroundColor = .yellow
        topLabel.font = UIFont.systemFont(ofSize: 30.0, weight: .regular)
        topLabel.text = "Sample Text"
        
        botLabel.textColor = .red
        botLabel.backgroundColor = .yellow
        botLabel.font = UIFont.systemFont(ofSize: 30.0 * 10.0, weight: .regular)
        botLabel.text = "Sample Text"
        
        topScrollView.delegate = self
        topScrollView.minimumZoomScale = 1
        topScrollView.maximumZoomScale = 10
        
        botScrollView.delegate = self
        botScrollView.minimumZoomScale = 0.1
        botScrollView.maximumZoomScale = 1
        
        topScrollView.zoomScale = topScrollView.minimumZoomScale
        botScrollView.zoomScale = botScrollView.minimumZoomScale

        view.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        topScrollView.backgroundColor = .white
        botScrollView.backgroundColor = .white

        topStatLabel.font = UIFont.systemFont(ofSize: 14, weight: .light)
        topStatLabel.numberOfLines = 0
        botStatLabel.font = UIFont.systemFont(ofSize: 14, weight: .light)
        botStatLabel.numberOfLines = 0
        
        let t = UITapGestureRecognizer(target: self, action: #selector(self.tapped(_:)))
        view.addGestureRecognizer(t)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        updateStatLabels()
    }
    
    func updateStatLabels() -> Void {
        var sTop = ""
        sTop += "Label Point Size: \(topLabel.font.pointSize)"
        sTop += "\n"
        sTop += "Label Frame: \(topLabel.frame)"
        sTop += "\n"
        sTop += "View Size: \(topView.bounds.size)"
        sTop += "\n"
        sTop += "Zoom Scale: \(String(format: "%0.1f", topScrollView.zoomScale))"
        
        var sBot = ""
        sBot += "Zoom Scale: \(String(format: "%0.1f", botScrollView.zoomScale))"
        sBot += "\n"
        sBot += "View Size: \(botView.bounds.size)"
        sBot += "\n"
        sBot += "Label Frame: \(botLabel.frame)"
        sBot += "\n"
        sBot += "Label Point Size: \(botLabel.font.pointSize)"
        
        topStatLabel.text = sTop
        botStatLabel.text = sBot
    }
    
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        if scrollView == topScrollView {
            return topView
        }
        return botView
    }
    
    @objc func tapped(_ g: UITapGestureRecognizer) -> Void {
        
        if Int(topScrollView.zoomScale) == Int(topScrollView.maximumZoomScale) {
            topScrollView.zoomScale = topScrollView.minimumZoomScale
        } else {
            topScrollView.zoomScale += 1
        }
        topScrollView.contentOffset = .zero

        // comparing floating point directly will fail, so round the values
        if round(botScrollView.zoomScale * 10) == round(botScrollView.maximumZoomScale * 10) {
            botScrollView.zoomScale = botScrollView.minimumZoomScale
        } else {
            botScrollView.zoomScale += 0.1
        }

        botScrollView.contentOffset = .zero

        updateStatLabels()
    }
    
}

The top scroll view has a 300 x 200 view with a 30-point label, allowing zoomScale from 1 to 10.

The bottom scroll view has a 3000 x 2000 view with a 300-point label, allowing zoomScale from 0.1 to 1.0.

Each time you tap the screen, the scrollViews increase zoomScale by 1 and 0.1 respectively.

And it looks like this at min-scale:

at 5 and 0.5 scale:

and at 10 and 1.0 scale:

这篇关于如何以编程方式使用UIGraphicsImageRenderer拍摄高质量的屏幕截图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆