Ios 如何以编程方式使用UIGraphicsImageRenderer拍摄高质量的屏幕截图?

Ios 如何以编程方式使用UIGraphicsImageRenderer拍摄高质量的屏幕截图?,ios,swift,uiimage,cgcontext,uigraphicsimagerenderer,Ios,Swift,Uiimage,Cgcontext,Uigraphicsimagerenderer,问题:拍摄屏幕截图后,通过缩放进行检查时,图像模糊。放大图像时,图像中的文字似乎模糊了 我知道这个问题已经被提过很多次了,但是没有一个是想要解决的。我已经查了不少类似的帖子 到目前为止,在这个论坛上分享的所有解决方案都是重复的或以任何其他方式相同的,但没有一个解决问题的方案 以下是我正在做的: extension UIView { func asImage() -> UIImage? { let format = UIGraphicsImageRendererFormat()

问题:拍摄屏幕截图后,通过缩放进行检查时,图像模糊。放大图像时,图像中的文字似乎模糊了

我知道这个问题已经被提过很多次了,但是没有一个是想要解决的。我已经查了不少类似的帖子

到目前为止,在这个论坛上分享的所有解决方案都是重复的或以任何其他方式相同的,但没有一个解决问题的方案

以下是我正在做的:

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

上述函数将UIView转换为和图像,但返回的图像质量不符合要求。

我正在我的一个应用程序中使用此代码,似乎工作正常。我不知道它的质量对你来说是否足够

import UIKit

extension UIApplication {

    var screenShot: UIImage?  {

        if let layer = keyWindow?.layer {
            let scale = UIScreen.main.scale

            UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
            if let context = UIGraphicsGetCurrentContext() {
                layer.render(in: context)
                let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return screenshot
            }
        }
        return nil
    }
}

我使用此扩展从视图UIGraphicsGetCurrentContext()创建图像,返回对当前图形上下文的引用。它不会创建一个。记住这一点很重要,因为如果您这样看它,您会发现它不需要size参数,因为当前上下文只是创建图形上下文时使用的大小

extension UIView {
   func toImage() -> UIImage? {
      UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)

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

      let image = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()

      return image
   }
}

执行
UIView
“图像捕获”将无法获得所需的结果

缩放
UIScrollView
时,它不会执行向量缩放。。。它执行光栅化缩放

您可以使用
ui标签
作为用于缩放的
视图,轻松确认这一点。这是一个30点系统字体的标签

1x
缩放时:

10x
缩放时:

该示例的代码:

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
    }

}
当您以
UIImage
的形式“捕获视图内容”时,您会得到一个位图,该位图是以屏幕比例的x点为单位的视图大小

因此,在iPhone 8上,例如,使用
@2x
屏幕比例,在
300 x 200
视图将被“捕获”为
UIImage
,像素为
600 x 400

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

您的评论包括:“……在编辑图像时……”——这是一个常见问题,我们希望允许用户添加文本(标签)、贝塞尔路径形状、添加图像等。例如,用户在屏幕上看到的可能是
3000 x 2000
像素的原始图像,显示在
300 x 200
点上。添加30点标签可能在屏幕上看起来不错,但如果将其作为
UIImage
(用于缩放或保存到磁盘)抓取,最终会成为
600 x 400
像素图像,当然,在较大的尺寸下看起来不太好

通常,解决这一问题的方法是:

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

  • 300x200
    视图中显示缩小的
    300x2000
    像素图像
  • 添加贝塞尔路径,矩形椭圆(20,20,200,200)
  • 在原点添加30点标签
    (32,32)
然后,在“捕获”该对象以进行输出/缩放时

  • 拍摄原始的
    3000 x 2000
    像素图像
  • 添加贝塞尔路径,矩形椭圆(20*10,20*10,200*10,200*10)
  • 在原点添加(30*10)-点标签
    (32*10,32*10)
另一个选项是进行屏幕编辑缩小

因此,您可以使用
300x200
图像视图,使用
300x2000
像素图像(按比例缩放)。当用户说“我想在rect
(20,20,200,200)
中添加椭圆贝塞尔路径时,您的代码将在图像本身的rect
(20*10,20*10,200*10,200*10)
处绘制椭圆,然后刷新图像视图的
.image
属性

这里有一个更详细的例子,有助于澄清问题:

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()
    }
    
}
顶部滚动视图有一个带有30点标签的
300 x 200
视图,允许从
1缩放到10

底部滚动视图有一个带有300点标签的
3000 x 2000
视图,允许从
0.1缩放到1.0

每次点击屏幕时,滚动视图将zoomScale分别增加1和0.1

在最小尺度下看起来是这样的:

在5和0.5刻度处:

在10和1.0刻度下:


你到底想用你的代码做什么?你截图?怎么做?你只是想调整它的大小?原始图像在哪里?请检查我的更新问题。我正在截图,或者你可以说将UIView转换为图像。但是返回的图像质量不好。这不是你问题的根源。我现在知道你想做什么。不。你只得到屏幕截图的视图大小。如果你把它调整到更大的尺寸,当然,结果图片会变得模糊。不,即使我不调整它的大小,如果不调整它的实际边界,它也会变得模糊。这是我的问题。我的意思是,当你正常看到它时,它看起来很完美,但是恩,你缩放它的像素不足以提供清晰的视觉效果。这看起来像是你使用了手机屏幕的大小。所以我猜当你导出这个屏幕截图并缩放它时,你会得到模糊的图像。是的,我使用了屏幕的大小,因为我在同一个屏幕上渲染图像,但是你可以根据你想要的大小调整比例。我做到了看不到模糊效果,但我会检查它。正如你在我的问题中看到的那样,我已经尝试过了。问题是,它会对可见区域进行屏幕截图,因此当你缩放它时,文本和图像的像素会变得模糊。这是不可接受的。感谢你提供的描述性答案。这意味着很多。但在我的应用程序中,我正在使用图像编辑器,我我不想向用户显示缩放的画布,我只有屏幕大小的固定边界。用户只能在该边界内添加内容。因此,我唯一的选择是创建一个ex的副本