1

I'm trying to convert my app's main view into PDF data using UIGraphicsPDFRenderer. The code sort of looks like this:

let pdfRenderer = UIGraphicsPDFRenderer()
let pdfData = pdfRenderer.pdfData { context in
  context.beginPage()
  view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
}

However, once I actually run this, it causes certain elements in my app to flicker every time drawHierarchy runs. this is what it looks like when run every second

I can change afterScreenUpdates to false to prevent this, but I need the latest update of the view in order for my capture to be accurate (I am hiding a subview from being captured)

I've also tried using view.layer.render(in: context), but the resulting capture isn't accurate (missing background colors).

Is there a way to avoid the flicker? I don't want to capture the password field or keyboard, but I definitely don't want them to be flickering.

Here is a very basic reproduction of the flashing behavior. If you have input in the password field and click "capture" the password field's input will flash.

Connor
  • 156
  • 1
  • 11
  • Can you share your project? it doesn't appear in mine – Niv Jul 20 '21 at 16:13
  • @Niv Here's a basic reproduction of what I'm seeing: https://github.com/connorlr/drawHierarchy-test If you click the "capture" button, it causes any input in the password field to flash – Connor Jul 20 '21 at 16:47
  • lol now I see... it's flickering because it's a password, apple made it on purpose so it would be more safe to share... – Niv Jul 20 '21 at 17:09
  • It also makes the keyboard overlay flicker, even if the keyboard overlay isn't in the view hierarchy that I'm rendering. Similarly, even if I set the root view to hidden, it still flickers even though it wouldn't be drawing the password field. – Connor Jul 20 '21 at 17:14

1 Answers1

0

Edit:

Tried to capture it this way and it even captures the password field.

enter image description here

class Capture {
    static func capture() {
        
        let directory = FileManager.default.urls(for: FileManager.SearchPathDirectory.cachesDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first!
        let filePath =  directory.appendingPathComponent("Test.pdf")
        print(filePath)
        
        if let rootView = UIApplication.shared.windows.first?.rootViewController?.view {
            let pdfRenderer = UIGraphicsPDFRenderer(bounds: rootView.bounds)
            try! pdfRenderer.writePDF(to: filePath, withActions: { (context) in
                context.beginPage(withBounds: rootView.bounds, pageInfo: [:])
                rootView.layer.render(in: context.cgContext)
            })
        }
    }
}

1st Attempt:

It's going to flicker because it hides the password and keyword while capturing the screen:

enter image description here

Code used:

class Capture {
    static func capture() {
        
        let directory = FileManager.default.urls(for: FileManager.SearchPathDirectory.cachesDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first!
        let filePath =  directory.appendingPathComponent("Test.pdf")
        print(filePath)
        
        if let rootView = UIApplication.shared.windows.first?.rootViewController?.view {
            let pdfRenderer = UIGraphicsPDFRenderer(bounds: rootView.bounds)
            try! pdfRenderer.writePDF(to: filePath, withActions: { (context) in
                context.beginPage(withBounds: rootView.bounds, pageInfo: [:])
                rootView.drawHierarchy(in: rootView.bounds, afterScreenUpdates: true)
            })
        }
    }
}
RTXGamer
  • 3,215
  • 6
  • 20
  • 29
  • I've updated my example to demonstrate my issue. Even if I'm not capturing the keyboard or the password field, they will still flicker inconsistently. – Connor Jul 20 '21 at 17:52
  • Thanks for the update. It does look like `view.layer.render(in: context.cgContext)` renders without flashing, but unfortunately the result has some visual issues with the rounded rectangle clipping on the inputs – Connor Jul 20 '21 at 18:39