Last active
September 9, 2023 12:53
-
-
Save otmb/0fd50f1d8d9d61d8c0c7ed2c19f7a68a to your computer and use it in GitHub Desktop.
When outputting PDF with SwiftUI, Table cannot be used with ImageRenderer, so use UIGraphicsPDFRenderer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ContentView: View { | |
@State var pdfUrl: URL? | |
var body: some View { | |
VStack { | |
if let pdfUrl = pdfUrl { | |
PreviewView(url: pdfUrl) | |
} | |
} | |
.padding() | |
.onAppear { | |
let outputFileURL = try! PdfPage().exportToPDF("sample.pdf", width: 340, height: 600) | |
pdfUrl = outputFileURL | |
} | |
} | |
} | |
struct PdfPage : View { | |
// https://stackoverflow.com/questions/75482532/how-can-i-display-tabular-data-with-swiftui-on-iphone | |
struct Stat: Identifiable, Equatable { | |
var id: String { name } | |
let name: String | |
let totalMatches: Int | |
let standingsPoints: Int | |
} | |
let stats = [ | |
Stat(name: "Fred", totalMatches: 12, standingsPoints: 87), | |
Stat(name: "Jim", totalMatches: 4, standingsPoints: 12), | |
Stat(name: "Dave", totalMatches: 9, standingsPoints: 91)] | |
var body: some View { | |
List { | |
Grid { | |
GridRow { | |
Text("Name") | |
Text("Matches") | |
Text("Points") | |
} | |
.bold() | |
Divider() | |
ForEach(stats) { stat in | |
GridRow { | |
Text(stat.name) | |
Text(stat.totalMatches, format: .number) | |
Text(stat.standingsPoints, format: .number) | |
} | |
if stat != stats.last { | |
Divider() | |
} | |
} | |
} | |
} | |
VStack(alignment: .leading) { | |
Text("Title").font(.largeTitle) | |
HStack { | |
ZStack(alignment: .top){ | |
VStack(spacing: 0) { | |
Rectangle() | |
.fill(.blue) | |
.frame(width: 240, height: 50) | |
.cornerRadius(10, corners: [.topLeft, .topRight]) | |
Rectangle() | |
.fill(.gray) | |
.frame(width: 240, height: 50) | |
} | |
HStack { | |
VStack { | |
Text("Hello") | |
Divider().padding(5).opacity(0) | |
Text("Hello") | |
} | |
.frame(width: 60, height: 100) | |
Rectangle() | |
.fill(.black) | |
.frame(width: 1, height: 100) | |
VStack { | |
Text("Hello") | |
Divider().padding(5).opacity(0) | |
Text("Hello") | |
} | |
.frame(width: 60, height: 100) | |
Rectangle() | |
.fill(.black) | |
.frame(width: 1, height: 100) | |
VStack { | |
Text("Hello") | |
Divider().padding(5).opacity(0) | |
Text("Hello") | |
} | |
.frame(width: 60, height: 100) | |
} | |
.overlay { | |
Rectangle() | |
.fill(.black) | |
.frame(width: 240, height: 1) | |
} | |
} | |
VStack{ | |
Image(systemName: "trash").font(.largeTitle) | |
Image(systemName: "trash").font(.largeTitle) | |
Image(systemName: "trash").font(.largeTitle) | |
Image(systemName: "trash").font(.largeTitle) | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} | |
struct RoundedCorner: Shape { | |
var radius: CGFloat | |
var corners: UIRectCorner | |
func path(in rect: CGRect) -> Path { | |
let path = UIBezierPath( | |
roundedRect: rect, | |
byRoundingCorners: corners, | |
cornerRadii: CGSize(width: radius, height: radius) | |
) | |
return Path(path.cgPath) | |
} | |
} | |
extension View { | |
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { | |
clipShape( RoundedCorner(radius: radius, corners: corners) ) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import PDFKit | |
struct PreviewView : UIViewRepresentable { | |
let url: URL | |
func makeUIView(context: Context) -> some PDFView { | |
let pdfView = PDFView() | |
pdfView.document = PDFDocument(url: url) | |
return pdfView | |
} | |
func updateUIView(_ uiView: UIViewType, context: Context) {} | |
} | |
func createUrl(fileName: String) throws -> URL { | |
let fileManager = FileManager.default | |
let url = fileManager.temporaryDirectory.appendingPathComponent(fileName, conformingTo: .pdf) | |
if fileManager.fileExists(atPath: url.path) { | |
try fileManager.removeItem(at: url) | |
} | |
return url | |
} | |
// https://www.hackingwithswift.com/forums/swift/creating-pdf-from-uiview-in-swift/442 | |
extension View { | |
func exportToPDF(_ fileName: String, width:CGFloat=595.2, height:CGFloat=841.8) throws -> URL { | |
let outputFileURL = try createUrl(fileName: fileName) | |
let pdfVC = UIHostingController(rootView: self) | |
pdfVC.view.frame = CGRect(x: 0, y: 0, width: width, height: height) | |
//Render the view behind all other views | |
let scenes = UIApplication.shared.connectedScenes | |
let windowScene = scenes.first as? UIWindowScene | |
let window = windowScene?.windows.first | |
let rootVC = window?.rootViewController | |
rootVC?.addChild(pdfVC) | |
rootVC?.view.insertSubview(pdfVC.view, at: 0) | |
//Render the PDF | |
let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: width, height: height)) | |
try pdfRenderer.writePDF(to: outputFileURL, withActions: { (context) in | |
context.beginPage() | |
rootVC?.view.layer.render(in: context.cgContext) | |
}) | |
pdfVC.removeFromParent() | |
pdfVC.view.removeFromSuperview() | |
return outputFileURL | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Result PDF