2

I am trying to convert image snapshots into a video but I am facing UI Thread problems: my view controller is locked. I would like to know how to handle this because I did a lot of research and tried to detach the process into different DispatchQueues but none of them worked. So, it explains why I am not using any Queue on the code below:

class ScreenRecorder {
func renderPhotosAsVideo(callback: @escaping(_ success: Bool, _ url: URL)->()) {
        var frames = [UIImage]()
        for _ in 0..<100 {
            let image = self.containerView.takeScreenshot()
            if let imageData = UIImageJPEGRepresentation(image, 0.7), let compressedImage = UIImage(data: imageData) {
                frames.append(compressedImage)
            }
        }
        self.generateVideoUrl(frames: frames, complete: { (fileURL: URL) in
            self.saveVideo(url: fileURL, complete: { saved in
                print("animation video save complete")
                callback(saved, fileURL)
            })
        })
    }
}

extension UIView {
    func takeScreenshot() -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
        let image = renderer.image { _ in
            self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
        }
        return image
    }
}

class ViewController {
let recorder = ScreenRecorder()

        recorder.renderPhotoAsVideo { success, url in

            if (success) {
                print("ok")
            } else {
                self.alert(title: "Erro", message: "Nao foi possivel salvar o video", block: nil)
            }
        }
}

PS: I used this tutorial as reference -> http://www.mikitamanko.com/blog/2017/05/21/swift-how-to-record-a-screen-video-or-convert-images-to-videos/

Vitor Venturin
  • 1,329
  • 1
  • 9
  • 13

1 Answers1

2

It really looks like this is not possible, at least not the way you are trying to do it. There are quite a few ways to render a UIViews content into an image, but all of them must be used from the main thread only. This applies also to the drawInHierarchy method you are using.

As you have to call it on the main thread and the method is just getting called so many times, I think this will never work in a performant way.

See profiling in Instruments: enter image description here

Sources:

d4Rk
  • 6,622
  • 5
  • 46
  • 60
  • In this case, takeScreenshot() should be done in Main Queue. – Vitor Venturin Mar 03 '18 at 15:12
  • 1
    But isn't the render stuff something that should be done in a background thread? – d4Rk Mar 04 '18 at 09:33
  • 1
    I just updated my answer according to the new information I could gather from your repo and further tests I did. Unfortunately I think it's not possible to do it this way. – d4Rk Mar 05 '18 at 09:31