You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中PDF缩略图生成器始终返回nil的问题求助

PDF缩略图生成器始终返回nil的排查与解决

请问有人能帮我解释一下,我所使用的PDF缩略图生成器为何始终返回nil?我希望在UITableView中展示PDF文件名的同时显示对应的缩略图,但尝试过的约4种生成器均仅返回nil。以下是我的Swift代码:

func uploadPDF() {
    let types = UTType.types(tag: "pdf",
                             tagClass: UTTagClass.filenameExtension,
                             conformingTo: nil)
    let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: types)
    documentPickerController.delegate = self
    self.present(documentPickerController, animated: true, completion: nil)
}

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    for url in urls {
        let thumbnail = thumbnailFromPdf(withUrl: url, pageNumber: 0)
        self.modelController.bidPDFUploadThumbnails.append(thumbnail!)
    }
    tableView.reloadData()
}

func thumbnailFromPdf(withUrl url:URL, pageNumber:Int, width: CGFloat = 240) -> UIImage? {
    guard let pdf = CGPDFDocument(url as CFURL),
        let page = pdf.page(at: pageNumber)
        else {
            return nil
    }

    var pageRect = page.getBoxRect(.mediaBox)
    let pdfScale = width / pageRect.size.width
    pageRect.size = CGSize(width: pageRect.size.width*pdfScale, height: pageRect.size.height*pdfScale)
    pageRect.origin = .zero

    UIGraphicsBeginImageContext(pageRect.size)
    let context = UIGraphicsGetCurrentContext()!

    // White BG
    context.setFillColor(UIColor.white.cgColor)
    context.fill(pageRect)
    context.saveGState()

    // Next 3 lines makes the rotations so that the page look in the right direction
    context.translateBy(x: 0.0, y: pageRect.size.height)
    context.scaleBy(x: 1.0, y: -1.0)
    context.concatenate(page.getDrawingTransform(.mediaBox, rect: pageRect, rotate: 0, preserveAspectRatio: true))

    context.drawPDFPage(page)
    context.restoreGState()

    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

核心问题与修复方案

1. 文件访问权限缺失

UIDocumentPicker返回的URL属于沙盒临时访问路径,必须主动申请权限才能读取文件内容,否则CGPDFDocument会直接返回nil。

修改documentPicker方法:

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    for url in urls {
        // 申请文件访问权限
        guard url.startAccessingSecurityScopedResource() else {
            print("无法访问目标PDF文件")
            continue
        }
        defer {
            url.stopAccessingSecurityScopedResource() // 用完后释放权限
        }
        if let thumbnail = thumbnailFromPdf(withUrl: url, pageNumber: 0) {
            self.modelController.bidPDFUploadThumbnails.append(thumbnail)
        } else {
            print("该PDF无法生成缩略图")
        }
    }
    tableView.reloadData()
}

2. PDF页码索引错误

CGPDFDocument的page(at:)方法是从1开始计数的,你传入的pageNumber:0会导致无法获取页面,直接触发guard分支返回nil。

修改thumbnailFromPdf方法的页码获取逻辑:

guard let pdf = CGPDFDocument(url as CFURL),
      // 转换为CGPDF要求的1起始索引
      let page = pdf.page(at: pageNumber + 1) else {
    print("PDF文件损坏或指定页码不存在")
    return nil
}

3. 绘图上下文兼容性问题

UIGraphicsBeginImageContext在Retina屏幕下会生成模糊图片,且存在上下文获取失败的风险,推荐使用iOS 10+引入的UIGraphicsImageRenderer替代。

修改后的完整thumbnailFromPdf方法:

func thumbnailFromPdf(withUrl url: URL, pageNumber: Int, width: CGFloat = 240) -> UIImage? {
    guard let pdf = CGPDFDocument(url as CFURL),
          let page = pdf.page(at: pageNumber + 1) else {
        return nil
    }

    let pageRect = page.getBoxRect(.mediaBox)
    let pdfScale = width / pageRect.size.width
    let targetSize = CGSize(width: pageRect.size.width * pdfScale, height: pageRect.size.height * pdfScale)

    // 使用UIGraphicsImageRenderer生成高清缩略图
    let renderer = UIGraphicsImageRenderer(size: targetSize)
    return renderer.image { context in
        let cgContext = context.cgContext
        // 填充白色背景
        cgContext.setFillColor(UIColor.white.cgColor)
        cgContext.fill(CGRect(origin: .zero, size: targetSize))
        
        cgContext.saveGState()
        // 调整坐标系适配PDF绘制方向
        cgContext.translateBy(x: 0, y: targetSize.height)
        cgContext.scaleBy(x: 1.0, y: -1.0)
        cgContext.concatenate(page.getDrawingTransform(.mediaBox, rect: CGRect(origin: .zero, size: targetSize), rotate: 0, preserveAspectRatio: true))
        
        cgContext.drawPDFPage(page)
        cgContext.restoreGState()
    }
}

4. 额外优化:UTType简化

iOS 14+可以直接使用系统定义的UTType.pdf,避免手动构造类型可能出现的匹配错误:

func uploadPDF() {
    let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: [.pdf])
    documentPickerController.delegate = self
    self.present(documentPickerController, animated: true, completion: nil)
}

内容的提问来源于stack exchange,提问作者Christian W

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:45:23