How to export a View in multiple A4 PDF pages?

I have been trying to find a way to export a View in SWIFTUI in A4 size pages PDF. The view cannot fit in one single page. So far I have managed to edit (with the help of ChatGPT) the code found in PDF Creator GitHub (See below)

But although I get multiple page PDF as a result only the first page is populated, the rest are just blank.

Does anyone faced something similar before, if yes how did you manage to export to PDF in A4 pages...

Thanks for any tips and help!

extension View{
    
    func sharePDF<Content: View> (@ViewBuilder content: @escaping () -> Content, fileName: String) {
        exportPDF(content: content, completion: { status , url in
            if let url = url, status {
                ShareSheet.instance.share(items: [url])
            } else {
                print("⚠️ Failed to make PDF")
            }
        }, fileName: fileName)
    }
    
    // MARK: Extracting View's Height and width with the Help of Hosting Controller and ScrollView
    fileprivate func convertToScrollView<Content: View>(@ViewBuilder content: @escaping ()->Content)->UIScrollView{
        
        let scrollView = UIScrollView()
        
        // MARK: Converting SwiftUI View to UIKit View
        let hostingController = UIHostingController(rootView: content()).view!
        hostingController.translatesAutoresizingMaskIntoConstraints = false
        
        // MARK: Constraints
        let constraints = [
        
            hostingController.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            hostingController.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            hostingController.topAnchor.constraint(equalTo: scrollView.topAnchor),
            hostingController.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            
            // Width Anchor
            hostingController.widthAnchor.constraint(equalToConstant: screenBounds().width)
        ]
        scrollView.addSubview(hostingController)
        scrollView.addConstraints(constraints)
        scrollView.layoutIfNeeded()
        
        return scrollView
    }
    
    // MARK: Export to PDF
    // MARK: Completion Handler will Send Status and URL
    fileprivate func exportPDF<Content: View>(@ViewBuilder content: @escaping () -> Content, completion: @escaping (Bool, URL?) -> (), fileName: String) {
        // MARK: Temp URL
        let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        // MARK: To Generate New File whenever it's generated
        let outputFileURL = documentDirectory.appendingPathComponent("\(fileName)\(UUID().uuidString).pdf")

        // MARK: PDF View
        let scrollView = convertToScrollView {
            content()
        }
        scrollView.tag = 1009
        scrollView.frame = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4 size in points (72 points per inch)

        let pageSize = scrollView.frame.size
        let contentSize = scrollView.contentSize
        let pageCount = Int(ceil(contentSize.height / pageSize.height))

        // Create PDF Context
        UIGraphicsBeginPDFContextToFile(outputFileURL.path, .zero, nil)

        // CHATGPT:
        for index in 0..<pageCount {
            // Begin new PDF page
            UIGraphicsBeginPDFPageWithInfo(CGRect(origin: .zero, size: pageSize), nil)

            // Calculate the visible frame for each page
            let visibleFrame = CGRect(x: 0, y: -pageSize.height * CGFloat(index), width: pageSize.width, height: pageSize.height)

            // Capture the screenshot of the visible content synchronously
            scrollView.clipToRect(visibleFrame) {
                // Take a screenshot of the visible content
                let screenshot = scrollView.takeScreenshot()

                // Draw the screenshot into the PDF context
                screenshot.draw(at: .zero)
            }
        }

        completion(true, outputFileURL)

        
        // End PDF Context
        UIGraphicsEndPDFContext()

        completion(true, outputFileURL)

        // Removing the added View
        getRootController().view.subviews.forEach { view in
            if view.tag == 1009 {
                print("Removed")
                view.removeFromSuperview()
            }
        }
    }
    
    fileprivate func screenBounds()->CGRect{
        return UIScreen.main.bounds
    }
    
    fileprivate 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
    }
    
    fileprivate func getSafeArea()->UIEdgeInsets{
        guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else{
            return .zero
        }
        
        guard let safeArea = screen.windows.first?.safeAreaInsets else{
            return .zero
        }
        
        return safeArea
    }
}

extension UIView {
    func takeScreenshot() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
        drawHierarchy(in: bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image ?? UIImage()
    }

    func clipToRect(_ rect: CGRect, perform: () -> Void) {
        guard let context = UIGraphicsGetCurrentContext() else { return }

        context.saveGState()
        context.clip(to: rect)
        perform()
        context.restoreGState()
    }
}
Post not yet marked as solved Up vote post of IFA Down vote post of IFA
708 views
  • No one ever faced the same issue? 😳

Add a Comment