使用 PDFKit 创建 PDF 文档时的共享问题

Share issue when using PDFKit to create a PDF document

我正在使用 PDFKit 创建 PDF 文件。

PDF 文档已正确创建,因为如果我尝试使用 PDFView 查看它,我会看到它 (@IBAction func DetailButton) 可能不是...我没看到正方形!

问题是我无法与 UIActivityViewController 共享它。 (@IBAction func Sh​​areButton)

我分享了一个文本文件,就是文件路径。 /var/mobile/Containers/Data/Application/....../Documents/1234.pdf

此外,当我分享到 'Mail' 时如何发送一个默认的电子邮件地址,当我分享到 'Message' 时如何发送一个 phone 号码?

func createPDFwithPDFKit(filePath:String, share:Bool) {

    let pdfTitle = "Swift-Generated PDF"
    let pdfMetadata = [
        // The name of the application creating the PDF.
        kCGPDFContextCreator: "XXX",
        // The name of the PDF's author.
        kCGPDFContextAuthor: "xxx",
        // The title of the PDF.
        kCGPDFContextTitle: game!.name,
        // Encrypts the document with the value as the owner password. Used to enable/disable different permissions.
        kCGPDFContextOwnerPassword: "myPassword123"
    ]

    // Creates a new PDF file at the specified path.
    UIGraphicsBeginPDFContextToFile(filePath, CGRect.zero, pdfMetadata)

    // Creates a new page in the current PDF context.
    UIGraphicsBeginPDFPage()

    // Default size of the page is 612x72.
    let pageSize = UIGraphicsGetPDFContextBounds().size
    let font = UIFont.preferredFont(forTextStyle: .largeTitle)

    // Let's draw the title of the PDF on top of the page.
    let attributedPDFTitle = NSAttributedString(string: pdfTitle, attributes: [NSAttributedString.Key.font: font])
    let stringSize = attributedPDFTitle.size()
    let stringRect = CGRect(x: (pageSize.width / 2 - stringSize.width / 2), y: 20, width: stringSize.width, height: stringSize.height)
    attributedPDFTitle.draw(in: stringRect)

    // Closes the current PDF context and ends writing to the file.
    UIGraphicsEndPDFContext()

    if share {
        let vc = UIActivityViewController(activityItems: [filePath], applicationActivities: [])
        vc.excludedActivityTypes = [
            UIActivity.ActivityType.assignToContact,
            UIActivity.ActivityType.saveToCameraRoll,
            UIActivity.ActivityType.postToFlickr,
            UIActivity.ActivityType.postToVimeo,
            UIActivity.ActivityType.postToTencentWeibo,
            UIActivity.ActivityType.postToTwitter,
            UIActivity.ActivityType.postToFacebook,
            UIActivity.ActivityType.openInIBooks
        ]
        present(vc, animated: true, completion: nil)
    }
}

@IBAction func ShareButton(_ sender: UIBarButtonItem) {



    alert.addAction(UIAlertAction(title: "Exporter au format PDF", style: .default, handler: { _ in

        let fileName = "1234.pdf"
        let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        let filePath = (documentsDirectory as NSString).appendingPathComponent(fileName) as String

        self.createPDFwithPDFKit(filePath:filePath, share:true)
    }))

    alert.addAction(UIAlertAction.init(title: "Annuler", style: .cancel, handler: nil))

    self.present(alert, animated: true, completion: nil)

}

@IBAction func DetailButton(_ sender: UIBarButtonItem) {
    // Create and add a PDFView to the view hierarchy.

    let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    let filePath = (documentsDirectory as NSString).appendingPathComponent("scores de '" + game!.name + "'.pdf") as String

    self.createPDFwithPDFKit(filePath:filePath, share:false)

    //View the PDF
    let pdfView = PDFView(frame: view.bounds)
    pdfView.autoScales = true
    pdfView.displayMode = .singlePageContinuous
    view.addSubview(pdfView)

    // Create a PDFDocument object and set it as PDFView's document to load the document in that view.
    let pdfDocument = PDFDocument(url: URL(fileURLWithPath: filePath))!
    pdfView.document = pdfDocument

}

在我自己使用 PDFKit 的过程中,我将 PDF 文本创建为 NSMutableAttributedString 的实例(就像您所做的那样),然后将其封装在 UISimpleTextPrintFormatter 的实例中。此对象处理文档的格式设置,尤其是在需要分页符时。此实例用于初始化 UIPrintPageRenderer 的实例。该对象 "knows" 关于页面大小、页面内的可打印区域和打印格式化程序。调用 UIGraphicsBeginPDFContextToData 后,您可以调用这些对象以逐页呈现 PDF。

显然这里发生了很多事情,所以我写了一个简单的应用程序来测试它。该场景有两个控件,一个系统样式的 "share" 按钮(在导航栏中)和一个覆盖整个安全区域并声明为 "PDFView" 类型的 UIView 实例。 PDF 仅包含文本:"This is text in a PDF file."。以下视图控制器逻辑提供了如何显示 PDF(在 viewDidLoad 方法中)以及如何完成共享(在 activityButton 操作中)的示例:

import UIKit
import PDFKit

class PDFViewController: UIViewController {

    // MARK: iVars
    private var reportBuffer = NSMutableData()

    // MARK: outlets and actions
    @IBOutlet weak var pdfReportView: PDFView!

    @IBAction func activityButton(_ sender: Any)
    {
        let activityViewController =
            getActivityViewController()
        self.present( activityViewController,
                      animated: true,
                      completion: nil )
    }

    // MARK: - Action convenience functions
    private func getActivityViewController() -> UIActivityViewController
    {
        let manager = FileManager.default
        let path = pdfURL.path
        if manager.fileExists(atPath: path ) {
            do {
                try manager.removeItem(at: pdfURL )
            } catch {
                fatalError()
            }
        }
        do {
            try reportBuffer.write(to: pdfURL, options: [.atomic])
        } catch {
            fatalError()
        }

        let activityVC =
            UIActivityViewController( activityItems: [ pdfURL ],
                                      applicationActivities: nil)
        activityVC.completionWithItemsHandler =
            {
                [weak self] activity, wasCompleted, returnedItems, error in
                if manager.fileExists(atPath: path ) {
                    do {
                        try manager.removeItem(at: self!.pdfURL )
                    } catch {
                        fatalError()
                    }
                }
            }
        activityVC.popoverPresentationController?.sourceView =
            self.pdfReportView
        activityVC.modalPresentationStyle = .popover
        return activityVC
    }

    // MARK: lifecycle
    override func viewDidAppear(_ animated: Bool)
    {
        super.viewDidAppear( animated )

        // create PDF content
        let report =
            NSMutableAttributedString(string: "This is text in a PDF file." )

        // create renderer
        let renderer = UIPrintPageRenderer()
        renderer.setValue( NSValue( cgRect: paperRect ),
                           forKey: "paperRect" )
        renderer.setValue( NSValue( cgRect: printableRect ),
                           forKey: "printableRect" )
        let printFormatter =
            UISimpleTextPrintFormatter( attributedText: report )
        renderer.addPrintFormatter(printFormatter,
                                   startingAtPageAt: 0)

        // draw the PDF into an NSMutableData buffer
        UIGraphicsBeginPDFContextToData( reportBuffer,
                                         paperRect,
                                         nil )
        let pageRange = NSMakeRange( 0, renderer.numberOfPages )
        renderer.prepare( forDrawingPages: pageRange )
        let bounds = UIGraphicsGetPDFContextBounds()
        for i in 0 ..< renderer.numberOfPages {
            UIGraphicsBeginPDFPage()
            renderer.drawPage( at: i, in: bounds )
        }
        UIGraphicsEndPDFContext()

        // display the PDF
        if let document =
            PDFDocument( data: reportBuffer as Data )
        {
            pdfReportView.document = document
        }
    }

    // MARK: - constants

    private struct Page
    {
        static let size =
            CGSize( width: 612.0, height: 792.0 )
        static let margins =
            UIEdgeInsets(top: 72.0,
                         left: 72.0,
                         bottom: 72.0,
                         right: 72.0)
    }
    private let printableRect =
        CGRect(x: Page.margins.left,
               y: Page.margins.top,
               width: Page.size.width
                - Page.margins.left
                - Page.margins.right,
               height: Page.size.height
                - Page.margins.top
                - Page.margins.bottom)

    private let paperRect =
        CGRect( x: 0.0,
                y: 0.0,
                width: Page.size.width,
                height: Page.size.height )

    private var pdfURL: URL {
        let manager = FileManager.default
        let url = manager.urls(for: .cachesDirectory,
                               in: .userDomainMask).first!
        return url.appendingPathComponent("temporary.pdf") as URL
    }
}

旁注:我对你的代码逻辑有点困惑。您使用 "ShareButton" 来显示警报,而警报又具有用于调用 UIActivityViewController 实例的按钮。您的业​​务需求可能会决定采用这种方法,但通常用户会发现这种方法出乎意料地复杂。点击共享按钮(系统图标是带有向上突出箭头的正方形)通常会直接调用 UIActivityViewController 的实例。也就是说,没有干预警报。

编辑: 我只能在真实设备上使用邮件和消息共享。 None 的模拟器设备在 UIActivityViewController 弹出窗口上提供了 Mail 或 Message 选项(尽管 Message 可用,至少在 iPad Air 模拟器上是这样)。