如何在后台线程上截取 UIView 的屏幕截图?

How to take a screenshot of UIView on a background thread?

我正在开发的应用几乎在每个屏幕上都有 google 地图。为了节省内存,我在各处重复使用相同的 google 地图视图。问题是,当你弹出一个 viewController 时,你可以在地图所在的位置看到一个白色的 space。为了解决这个问题,我正在截取它的屏幕截图并在删除地图之前添加为背景。但还有另一个问题,在 iPhoneX 上截屏大约需要 0.3 秒(我想在旧手机上更糟)。有什么方法可以在后台线程上截取 UIView 的屏幕截图吗?

来自 UIKit documentation

Use UIKit classes only from your app’s main thread or main dispatch queue, unless otherwise indicated. This restriction particularly applies to classes derived from UIResponder or that involve manipulating your app’s user interface in any way.

我认为没有办法在后台线程上对视图进行快照,因为您使用的是 UIKit 方法。

事实上,这是可能的!但首先在 UI Thread 上,您需要获取一些信息,如下所示:

CALayer* layer = view.layer;
CGRect frame = view.frame;

,然后切换到后台线程使用下面的代码获取图像:

UIGraphicsBeginImageContextWithOptions(frame.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

我使用 swift 尝试了所有最新的快照方法。其他方法在后台对我不起作用。但是以这种方式拍摄快照对我有用。

创建带有参数视图层和视图边界的扩展。

extension UIView {
    func asImageBackground(viewLayer: CALayer, viewBounds: CGRect) -> UIImage {
        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(bounds: viewBounds)
            return renderer.image { rendererContext in
                viewLayer.render(in: rendererContext.cgContext)
            }
        } else {
            UIGraphicsBeginImageContext(viewBounds.size)
            viewLayer.render(in:UIGraphicsGetCurrentContext()!)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return UIImage(cgImage: image!.cgImage!)
        }
    }
}

用法

DispatchQueue.main.async {
                let layer = self.selectedView.layer
                let bounds = self.selectedView.bounds
                DispatchQueue.global(qos: .background).async {
                    let image = self.selectedView.asImageBackground(viewLayer: layer, viewBounds: bounds)
                }
            }

我们需要在主线程中计算layer和bounds,其他操作在后台线程中进行。它将在 UI.

中提供流畅的用户体验,没有任何滞后或中断。