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

2.6k Views Asked by At

The app I'm working on has google maps on almost every screen. To save memory, I'm reusing the same google maps view everywhere. The problem is, when you pop a viewController, you can see a white space where the map was. To work around that I'm taking screenshots of it and adding as a background before removing the map. But there's another problem, taking a screenshot takes about 0.3 seconds on iPhoneX (I suppose it's even worse on older phones). Is there any way to take screenshots of UIView on a background thread?

3

There are 3 best solutions below

2
On BEST ANSWER

Actually ,it is possible! But first on UIThread you need get some infos Like this:

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

, then change to backgroundthead use codes below to get image:

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

From 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.

I don't think there is a way of snapshoting the view on the background thread because you are using an UIKit method.

0
On

I tried all the latest snapshot methods using swift. Other methods didn't work for me in the background. But taking snapshot this way worked for me.

create an extension with parameters view layer and view bounds.

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

Usage

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

We need to calculate layer and bounds in the main thread, then other operations will work in the background thread. It will give smooth user experience without any lag or interruption in UI.