我正在开发一个 SwiftUI 应用程序,其中有一个 FileInfoViewModel 类负责管理 PDF 文件。该类从 Documents 目录中获取 PDF 文件,我尝试将每个 PDF 的第一页预加载并显示为 UIImage。
这是我的 FileInfoViewModel 类的简化版本:
struct PDFFileInfo: Identifiable {
let id = UUID()
let name: String
let pageCount: Int
let creationDate: Date
let fileURL: URL // Add fileURL property
}
class FileInfoViewModel: ObservableObject {
@Published var pdfFiles: [PDFFileInfo] = [] // Store PDF file info
@Published var preloadedImages: [String: UIImage] = [:]
init() {
fetchPDFFiles()
}
// Function to fetch PDF files from the Documents directory based on date range
func fetchPDFFiles() {
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
do {
let fileURLs = try FileManager.default.contentsOfDirectory(at: documentsDirectory, includingPropertiesForKeys: nil, options: [])
// Filter and process PDF files within the date range
for fileURL in fileURLs {
if fileURL.pathExtension.lowercased() == "pdf" {
if let pdfDocument = PDFDocument(url: fileURL),
let creationDate = try? fileURL.resourceValues(forKeys: [.creationDateKey]).creationDate {
// Check if the creation date is within the specified date range
let fileInfo = PDFFileInfo(name: fileURL.lastPathComponent, pageCount: pdfDocument.pageCount, creationDate: creationDate, fileURL: fileURL)
self.pdfFiles.append(fileInfo)
}
if let pdfDocument = PDFDocument(url: fileURL), let firstPage = pdfDocument.page(at: 0) {
self.preloadedImages[fileURL.lastPathComponent] = self.firstPageToImage(firstPage)
}
}
}
}
// Sort pdfFiles array by creation date in chronological order
pdfFiles.sort(by: { $0.creationDate > $1.creationDate })
// Group pdfFiles by creation date
// groupedPDFFiles = Dictionary(grouping: pdfFiles, by: { Calendar.current.startOfDay(for: $0.creationDate) })
} catch {
// Handle error
print("Error while fetching PDF files: \(error)")
}
}
}
private func firstPageToImage(_ page: PDFPage) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: page.bounds(for: .mediaBox).size)
return renderer.image { context in
page.draw(with: .mediaBox, to: context.cgContext)
}
}
}
如您所见,我在此函数中预加载它们并附加
preloadedImages
数组。但是当我的应用程序启动并再次加载文件时,我在此过程中遇到了滞后。所以问题是 - 如何异步加载它们,这样当它还没有准备好时,它可以显示 ProgressView 或类似的东西。
ForEach(fileInfoViewModel.pdfFiles, id: \.id) { file in
// image: firstPageToImage(firstPage),
if let uiImage = fileInfoViewModel.preloadedImages[file.name] {
FileCellView(title: file.name, dateOfCreation: dateFormatterStore.dateFormatter.string(from: file.creationDate), fileSize: fileSizeInMB(file.fileURL)) {
} delete: {
} share: {
}
}
}
struct FileCellView: View {
@State var image: UIImage? = UIImage(named: "1")!
@State var title: String = "Scanner 1"
@State var dateOfCreation: String = "31.02.2023"
@State var fileSize: String = "132 Gb"
let edit: () -> Void
let delete: () -> Void
let share: () -> Void
var body: some View {
HStack {
if image == image {
Image(uiImage: image!)
.resizable()
.scaledToFit()
.cornerRadius(4)
.padding(.trailing)
}
}
.frame(height: 120)
.padding(15)
.background(Color(red: 0.14, green: 0.14, blue: 0.14))
.cornerRadius(12)
}
}
您可以尝试这种简单的方法,测试 FileInfoViewModel
pdfFiles
的 isEmpty
数组是否,
如果是,则显示 ProgressView()
,否则显示 FileCellView(...)
。
struct ContentView: View {
@StateObject var fileInfoViewModel = FileInfoViewModel()
var body: some View {
VStack {
if fileInfoViewModel.pdfFiles.isEmpty {
ProgressView()
} else {
ForEach(fileInfoViewModel.pdfFiles, id: \.id) { file in
// ...
}
}
}
}
}