ios 将SwiftUI视图导出为PDF

pb3skfrl  于 2023-03-09  发布在  iOS
关注(0)|答案(1)|浏览(171)

我在将WorkListView导出为PDF时遇到了问题。下面是我的代码的一个简单示例:
客户端数据

class Client: ObservableObject,
                            Identifiable {
    let name = "Foo"
    @Published var work: [Work]
    
    init() {
        self.work = []
    }

    func addWork(_ work: Work) {
        self.work.append(work)
    }
}

工作数据

struct Work: Identifiable {
    var id = UUID()
    
    let content = "Bar"
}

工作列表视图

struct WorkListView: View {
    @State var client: Client
    @ObservedObject var clientData = ClientData()
    @State private var showSheet = false
    var body: some View {
VStack {
                Button {
                    exportPDF {
                        self
                    } completion: { status, url in
                        if let url = url, status {
                            self.PDFUrl = url
                            self.showShareSheet.toggle()
                        }
                        else {
                            print("Failed to produce PDF")
                        }
                    }
                } label: {
                    Image(systemName: "square.and.arrow.up.fill")
                        .font(.title3)
                }
            }
        List {
    Section {
        Text(client.name)
    }
    Section(header: Text("Performed Work")) {
        ForEach(client.work.indices) { work in
            VStack  {
                Text(client.work[work].content)
            }
        }
    }
        }
        .onTapGesture {
            showSheet.toggle()
        }
        .sheet(isPresented: $showSheet, content: {
            AddWorkView(client: client, showSheet: $showSheet)
        })
        .padding()
    }
}

导出为PDF的功能

extension View {
    func convertToScrollView<Content: View>(@ViewBuilder content: @escaping ()->Content)->UIScrollView {
            
        let scrollView = UIScrollView()
            
        let hostingController = UIHostingController(rootView: content()).view!
        hostingController.translatesAutoresizingMaskIntoConstraints = false
            
        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),
                
            hostingController.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width)
        ]
        scrollView.addSubview(hostingController)
        scrollView.addConstraints(constraints)
        scrollView.layoutIfNeeded()
            
        return scrollView
    }

    func exportPDF<Content: View>(@ViewBuilder content: @escaping ()->Content, completion: @escaping (Bool,URL?)->()){

        let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        let outputFileURL = documentDirectory.appendingPathComponent("YOURPDFNAME\(UUID().uuidString).pdf")

        let pdfView = convertToScrollView {
            content()
        }
        pdfView.tag = 1009
        let size = pdfView.contentSize
        pdfView.frame = CGRect(x: 0, y: getSafeArea().top, width: size.width, height: size.height)

        getRootController().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)
        }

        getRootController().view.subviews.forEach { view in
            if view.tag == 1009 {
                print("Removed")
                view.removeFromSuperview()
            }
        }
    }

    
    func screenBounds()->CGRect {
        return UIScreen.main.bounds
    }
    
    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
    }
    
    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
    }
}

我还有一个用于切换shareSheet的结构体。
所以这是我的工作列表视图,我希望能够导出到PDF。我只能设法导出它只是按钮。有人可以帮助我吗?

rt4zxlrg

rt4zxlrg1#

以下是将SwiftUI视图渲染为PDF的方法:

这是我们要保存的视图:
struct SomeView: View {
    var body: some View {
        Text("Some text")
            .foregroundColor(.white)
            .padding(200)
            .background(Color.black)
    }
}
PDF管理器
@MainActor final class PDFManager: ObservableObject {
    
    func saveAsPDF(view: any View) {
        
        // Render your desired view.
        let renderer = ImageRenderer(content: AnyView(view) )
        
        // Save to documents directory
        let url = URL.documentsDirectory.appending(path: "output.pdf")
        
        // Start the rendering process
        renderer.render { size, context in
            
            // Tell SwiftUI our PDF should be the same size as the views we're rendering
            var box = CGRect(x: 0, y: 0, width: size.width, height: size.height)
            
            // 5: Create the CGContext for our PDF pages
            guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else {
                return
            }
            
            // Start a new PDF page
            pdf.beginPDFPage(nil)
            
            // Render the SwiftUI view data onto the page
            context(pdf)
            
            // End the page and close the file
            pdf.endPDFPage()
            pdf.closePDF()
        }
    }
}
用法
struct ContentView: View {
    
    @StateObject private var pdfManager = PDFManager()
    
    var body: some View {
        Button("Save as PDF") {
            pdfManager.saveAsPDF(view: SomeView() )
        }
    }
}

希望这对你有帮助:)

相关问题