如何在 SwiftUI 中高效预加载并显示 PDF 文件的首页图像?

问题描述 投票:0回答:1

我正在开发一个 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)

    }
}
swift swiftui pdfkit
1个回答
0
投票

您可以尝试这种简单的方法,测试 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
                    // ...
                }
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.