79337191

Date: 2025-01-07 20:02:49
Score: 1
Natty:
Report link

I have created a working variant, but it works only once.

When I try to call it again, it doesn't work. Seems like something with ViewController lifecycle.

I will be grateful for your help. Maybe we can beat it together.

extension View {
func exportPDF<Content: View>(@ViewBuilder content: @escaping () -> Content, completion: @escaping (Bool, URL?) -> ()) {
    let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
    let dateString = generateUniqueStringFromCurrentTime()
    let outputFileURL = documentDirectory.appendingPathComponent("MyFile-\(dateString).pdf")
    
    let rootVC = getRootController()
    
    let pdfView = convertToScrollView {
        content()
    }
    pdfView.tag = 123
    let size = pdfView.contentSize
    pdfView.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    
    // Insert the View to RootView to render PDF and remove from it afterwards.
    rootVC.view.insertSubview(pdfView, at: 0)
    let renderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    
    do {
        try renderer.writePDF(to: outputFileURL, withActions: { context in
            context.beginPage()
            pdfView.layer.render(in: context.cgContext)
        })
        completion(true, outputFileURL)
    } catch {
        completion(false, nil)
        print(error.localizedDescription)
    }
    
    rootVC.view.subviews.forEach { view in
        if view.tag == 123 {
            view.removeFromSuperview()
        }
    }
}

private func convertToScrollView<Content: View>(@ViewBuilder content: @escaping () -> Content) -> UIScrollView {
    let scrollView = UIScrollView()
    
    let hostController = UIHostingController(rootView: content()).view!
    hostController.translatesAutoresizingMaskIntoConstraints = false
    
    let constraints = [
        hostController.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
        hostController.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
        hostController.topAnchor.constraint(equalTo: scrollView.topAnchor),
        hostController.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
        
        hostController.widthAnchor.constraint(equalToConstant: screenBounds().width)
    ]
    
    scrollView.addSubview(hostController)
    scrollView.addConstraints(constraints)
    scrollView.layoutIfNeeded()
    
    return scrollView
}

private func getRootController() -> UIViewController {
    guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
        return .init()
    }
    
    guard let root = screen.windows.first?.rootViewController else {
        return .init()
    }
    
    return root
}

private func screenBounds() -> CGRect {
    return UIScreen.main.bounds
}

private func generateUniqueStringFromCurrentTime() -> String {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd-HH-mm-ss-SSS" // Format: Year-Month-Day-Hour-Minute-Second-Millisecond
    let dateString = dateFormatter.string(from: Date())
    return dateString
}
Reasons:
  • RegEx Blacklisted phrase (2): I will be grateful
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (0.5):
Posted by: Ilya Biltuev