Reputation: 171
I have a SwiftUI app that successfully exports a PDF and persists the data in SwiftData. I am trying to add a clickable link inside of the exported PDF.
I tried using a link view in exported view but that did not work.
Not sure if PDFAnnotation would be useful or how to use in this case.
Is there a simpler way to do this?
import SwiftUI
import PDFKit
import UIKit
struct InvoiceDetailsView: View {
@Environment(\.modelContext) var modelContext
let invoice: Invoice
var body: some View {
// This is the view that gets exported see below
var invoicePrintView = InvoicePrintView(
// Other stuff not relevant
invoice: invoice
)
NavigationStack {
VStack(spacing:0){
ScrollView(.vertical, showsIndicators: false){
invoicePrintView
.background(
Rectangle()
.fill(Color.white)
.shadow(color: .gray, radius: 2, x: 0, y: 2))
}
}
.toolbar(content: {
ToolbarItem(placement: .navigationBarTrailing) {
HStack {
Button{
invoicePrintView.space = 50
invoicePrintView.previewSize = 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
// See extension on view below
invoicePrintView.exportToPDF(filename: invoice.invoiceNumber ) { url in
if let urlString = url, let _ = try? Data(contentsOf: URL(fileURLWithPath: urlString)) {
let share = UIActivityViewController(activityItems: [URL(fileURLWithPath: urlString)], applicationActivities: nil)
guard let firstScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return
}
guard let firstWindow = firstScene.windows.first else {
return
}
firstWindow.rootViewController?.present(share, animated: true, completion: nil)
}
}
}
} label: {
Image(systemName: "square.and.arrow.up")
}
}
}
})
}
}
}
struct InvoicePrintView: View {
let invoice: Invoice
var body: some View {
VStack(spacing:0) {
VStack {
// THIS IS WHERE LINK IS CREATED
if invoice.hasPaymentLink == true {
HStack{
VStack(alignment: .leading) {
Text("Pay your invoice")
Text("\(invoice.paymentLinkText ?? "")")
}
}
}
// Other Stuff in geometrey reader
}
}
}
}
// Extension on view
extension View {
func exportToPDF(filename: String, completion: @escaping (_ url: String?) -> Void) {
let outputFileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Invoice \(filename).pdf")
let pageSize = CGSize(width: AppConfig.pageWidth*2, height: AppConfig.pageHeight*2)
let hostingController = UIHostingController(rootView: self)
hostingController.view.frame = CGRect(origin: .zero, size: pageSize)
guard let firstScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return
}
guard let firstWindow = firstScene.windows.first else {
return
}
guard let root = firstWindow.rootViewController else { return }
root.addChild(hostingController)
root.view.insertSubview(hostingController.view, at: 0)
let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(origin: .zero, size: pageSize))
DispatchQueue.main.async {
do {
try pdfRenderer.writePDF(to: outputFileURL, withActions: { (context) in
context.beginPage()
hostingController.view.layer.render(in: context.cgContext)
})
if AppConfig.showSavedPDFLocation {
print("PDF file saved to:\n\(outputFileURL.path)")
}
completion(outputFileURL.path)
} catch {
print("Could not create PDF file: \(error.localizedDescription)")
completion(nil)
}
hostingController.removeFromParent()
hostingController.view.removeFromSuperview()
}
}
}
Upvotes: 2
Views: 177