3

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?

Senõr Ganso
  • 1,694
  • 16
  • 23
  • https://stackoverflow.com/questions/42778797/how-to-take-a-screenshot-of-a-pickerviewcontroller-and-uiview/42786207#42786207 . refere this my answer – Mahesh Dangar Oct 02 '18 at 12:44
  • You downvoted my question, but your answer has nothing to do with it. Read again. I need to take a screenshot on a BACKGROUND thread. Your answer works only on the main thread. – Senõr Ganso Oct 02 '18 at 13:02
  • hey why would i downvote your question buddy i am here to help other people i don't have anything to do with your question and i have not downvote your question – Mahesh Dangar Oct 02 '18 at 13:04
  • and i dont think you can capture screenshot in background thread you must have to do it in main thread https://stackoverflow.com/questions/52605016/difference-between-dispatchqueue-types-in-swift – Mahesh Dangar Oct 02 '18 at 13:10

3 Answers3

6

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.

Faris Muhammed
  • 970
  • 13
  • 17
4

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();
Hongwei Lv
  • 56
  • 4
  • I keep receiving this message "This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.". But everything seems to work, though. – Senõr Ganso Oct 18 '18 at 11:14
  • But actually this method of taking a screenshot is fast enough that I don't seem to even need a background thread. – Senõr Ganso Oct 18 '18 at 11:19
-1

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.