UIMarkupTextPrintFormatter 和 Mac Catalyst

Posted

技术标签:

【中文标题】UIMarkupTextPrintFormatter 和 Mac Catalyst【英文标题】:UIMarkupTextPrintFormatter and Mac Catalyst 【发布时间】:2020-03-15 16:55:59 【问题描述】:

我有一个 iPad 应用程序,已使用 Catalyst 成功迁移到 Mac。

虽然我可以在 iPad/iPhone 上使用 UIMarkupTextPrintFormatter 生成 PDF,但它在 Mac 上却无法正常工作。

事实上,我什至无法构建 Mac 二进制文件,除非我使用 #if !targetEnvironment(macCatalyst) 注释掉 UIMarkupTextPrintFormatter,因为 Xcode 只会显示错误:

架构 x86_64 的未定义符号: “_OBJC_CLASS_$_UIMarkupTextPrintFormatter”,引用自: Functions.o ld 中的 objc-class-ref:未找到体系结构 x86_64 的符号 clang:错误:链接器命令失败,退出代码 1(使用 -v 查看调用)

令人困惑的是,Apple 的文档表明它与 Mac Catalyst 13.0+ 兼容 https://developer.apple.com/documentation/uikit/uimarkuptextprintformatter

有其他人经历过这种情况吗?您是否能够找到解决方案?

谢谢。

编辑:根据 Sam Wize 在此处的帖子,我找到了一个出色的解决方案,无需修改即可在 macCatalyst 中使用:

https://samwize.com/2019/07/02/how-to-generate-pdf-with-images/

关键是使用 WKWebView 对象(但不显示它)作为中介来加载 html 文件,然后使用它的 viewPrintFormatter 通过其 didFinish navigation: 委托来呈现 PDF

这是我的代码(希望 cmets 是不言自明的)。使用以下代码创建一个名为 PDFCreator.swift 的 Swift 文件:

import WebKit

typealias PDFCompletion = (Result<NSData, Error>) -> Void

class PDFCreator: NSObject 
var webView: WKWebView? = nil
var completion: PDFCompletion!

func exportPDF(html: String, completion: @escaping PDFCompletion) throws 
    // Set up the completion handler to be called by the function in the delegate method
    // It has to be instantiated here so the delegate method can access it
    self.completion = completion
    // Creates a WebKit webView to load the HTML string & sets the delegate (self) to respond
    let webView = WKWebView()
    webView.navigationDelegate = self
    // If the other assets are in the same baseURL location (eg. Temporary Documents Directory, they will also render)
    // But you need to ensure the assets are already there before calling this function
    let baseURL = URL(fileURLWithPath: NSTemporaryDirectory())
    // Loads the HTML string into the WebView and renders it (invisibly) with any assets
    webView.loadHTMLString(html, baseURL: baseURL)
    self.webView = webView
    // After this function closes, the didFinish navigation delegate method is called
    


func createPDF(_ formatter: UIViewPrintFormatter) 
    // Subclass UIPrintPageRenderer if you want to add headers/footers, page counts etc.
    let printPageRenderer = UIPrintPageRenderer()
    printPageRenderer.addPrintFormatter(formatter, startingAtPageAt: 0)

    // Assign paperRect and printableRect
    // A4, 72 dpi
    let paperRect = CGRect(x: 0, y: 0, width: 595.2, height: 841.8)
    let padding: CGFloat = 20
    let printableRect = paperRect.insetBy(dx: padding, dy: padding)
    printPageRenderer.setValue(printableRect, forKey: "printableRect")
    printPageRenderer.setValue(paperRect, forKey: "paperRect")
    // Assign header & footer dimensions
    printPageRenderer.footerHeight = 70
    printPageRenderer.headerHeight = 20

    // Create PDF context and draw
    let pdfData = NSMutableData()
    UIGraphicsBeginPDFContextToData(pdfData, .zero, nil)
    for i in 0..<printPageRenderer.numberOfPages 
        UIGraphicsBeginPDFPage();
        printPageRenderer.drawPage(at: i, in: UIGraphicsGetPDFContextBounds())
    
    UIGraphicsEndPDFContext();

    // Send the PDF data out with a Result of 'success' & the NSData object for processing in the completion block
    self.completion?(.success(pdfData))
    



extension PDFCreator: WKNavigationDelegate 
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) 
    let viewPrintFormatter = webView.viewPrintFormatter()
    createPDF(viewPrintFormatter)
    

在我的应用程序中,我实例化了一个 PDFCreator 对象

let pdfCreator = PDFCreator()

然后我确保首先在相同的“baseURL”位置创建 HTML 文件所需的所有本地资源 - 在我的例子中是 NSTemporaryDirectory() - 然后运行以下命令:

let pdfFilePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("test.pdf")

 try? pdfCreator.exportPDF(html: htmlString, completion:  (result) in
         switch result 
         case .success(let data):
                try? data.write(to: pdfFilePath, options: .atomic)
                // *** Do stuff with the file at pdfFilePath ***

         case .failure(let error):
                print(error.localizedDescription)
            
        )

【问题讨论】:

我还向 Apple 提交了错误报告,所以我们会看看他们怎么说。 赞成,因为我有完全相同的问题。感谢您对如何评论它的建议。遗憾的是我还没有找到任何解决方案,所以它可能确实是一个 Apple 错误。 谢谢。一旦我对此有答案,我会在这里发布! 仍然没有在 13.3 和 Xcode 11.3 中修复:-/ 找到了解决方案(参见上面的编辑)。它更优雅,可与 macCatalyst 一起使用,并从 HTML 生成 PDF 文件,带有图像! 【参考方案1】:

我也有同样的问题。但是我可以通过使用 Swift 的函数将 html 转换为属性文本,然后使用 UISimpleTextPrintFormatter 和属性文本来解决它。

我的原始代码:

let formatter = UIMarkupTextPrintFormatter(markupText: htmlString)
formatter.perPageContentInsets = UIEdgeInsets(top: 70.0, left: 60.0, bottom: 70.0, right: 60.0)
printController.printFormatter = formatter
printController.present(animated: true, completionHandler: nil)

使用 Catalyst(和 ios):

guard let printData = htmlString.data(using: String.Encoding.utf8) else  return 
do 
    let printText =  try NSAttributedString(data: printData, options: [.documentType: NSAttributedString.DocumentType.html,  .characterEncoding: String.Encoding.utf8.rawValue],  documentAttributes: nil)
        
    let formatter = UISimpleTextPrintFormatter(attributedText: printText)
    formatter.perPageContentInsets = UIEdgeInsets(top: 70.0, left: 60.0, bottom: 70.0, right: 60.0)
    printController.printFormatter = formatter
    printController.present(animated: true, completionHandler: nil)
 catch 
     print(error)

但是,NSAttributedString(data: ) 似乎对 Catalyst 上的内容比在 iOS 上更敏感。例如,我是否对在 iOS 上运行良好的表格有问题。所以这不是一个完美的解决方案。

编辑 似乎可以处理的更好的解决方案,例如更好的表是:

func compHandler(attributedString:NSAttributedString?, attributeKey:[NSAttributedString.DocumentAttributeKey : Any]?, error:Error?) -> Void 
    guard let printText = attributedString else  return 
    let formatter = UISimpleTextPrintFormatter(attributedText: printText)
    formatter.perPageContentInsets = UIEdgeInsets(top: 70.0, left: 60.0, bottom: 70.0, right: 60.0)
    printController.printFormatter = formatter
    printController.present(animated: true, completionHandler: nil)

        
guard let printData = htmlString.data(using: String.Encoding.utf8) else  return 
NSAttributedString.loadFromHTML(data: printData, options: [.documentType: NSAttributedString.DocumentType.html,  .characterEncoding: String.Encoding.utf8.rawValue], completionHandler: compHandler)

【讨论】:

感谢这个解决方法,虽然我大量使用表格意味着它并没有真正起作用。他们用 iOS 13.4 解决了这个问题。我目前的解决方法是将其导出为 HTML(即根本不将其呈现为 PDF),但这意味着我无法添加我的图像/图表。 找到了解决方案(参见上面的编辑)。它更优雅,可与 macCatalyst 一起使用,并从 HTML 生成 PDF 文件,带有图像!

以上是关于UIMarkupTextPrintFormatter 和 Mac Catalyst的主要内容,如果未能解决你的问题,请参考以下文章