UIMarkupTextPrintFormatter和Mac Catalyst [英] UIMarkupTextPrintFormatter and Mac Catalyst
问题描述
我有一个iPad应用程序,已经使用Catalyst成功转移到Mac.
I have an iPad application which I've successfully moved to Mac using Catalyst.
虽然我可以使用 UIMarkupTextPrintFormatter
在iPad/iPhone上生成PDF,但在Mac上确实应有的作用.
While I can generate PDFs on the iPad/iPhone using UIMarkupTextPrintFormatter
, it doesn't work on the Mac when it really should.
实际上,除非我使用 #if!targetEnvironment(macCatalyst)
注释掉 UIMarkupTextPrintFormatter
,否则我什至无法构建Mac二进制文件,因为Xcode只会显示错误:
In fact, I cannot even build the Mac binary unless I comment out UIMarkupTextPrintFormatter
using #if !targetEnvironment(macCatalyst)
as Xcode simply presents an error:
体系结构x86_64的未定义符号:
从以下位置引用的"_OBJC_CLASS _ $ _ UIMarkupTextPrintFormatter"在Functions.o中的objc-class-ref ld:找不到针对架构x86_64的符号clang:错误:链接器命令失败,并带有退出代码1(使用-v查看调用)
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_UIMarkupTextPrintFormatter", referenced from: objc-class-ref in Functions.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
这令人困惑,因为苹果公司的文档表明它与Mac Catalyst 13.0+兼容 https://developer.apple.com/documentation/uikit/uimarkuptextprintformatter
It's confusing as Apple's documentation suggests it is compatible with Mac Catalyst 13.0+ https://developer.apple.com/documentation/uikit/uimarkuptextprintformatter
其他人有没有遇到过这种情况,您是否能够找到解决方案?
Has anyone else experienced this and were you able to find a solution?
谢谢.
根据Sam Wize在此处的帖子,我发现了一个出色的解决方案,该解决方案也无需修改即可在macCatalyst中工作:
https://samwize.com/2019/07/02/how-to-generate-pdf-with-images/
https://samwize.com/2019/07/02/how-to-generate-pdf-with-images/
关键是使用WKWebView对象(但不显示该对象)作为中介来加载HTML文件,然后使用其viewPrintFormatter通过其 didFinish导航:
委托来呈现PDF.
The key is to use a WKWebView object (but not show it) as an intermediary to load the HTML file, then use it's viewPrintFormatter to render a PDF via its didFinish navigation:
delegate
这是我的代码(希望注释是不言自明的).使用以下代码创建一个名为PDFCreator.swift的Swift文件:
Here is my code (hopefully the comments are self explanatory). Create a a Swift file called PDFCreator.swift with the following code:
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对象
In my App I instantiate a PDFCreator object
let pdfCreator = PDFCreator()
然后,我确保首先在相同的'baseURL'位置中创建HTML文件所需的所有本地资产-在我的情况下为 NSTemporaryDirectory()
-然后运行以下命令:
Then I ensure all the local assets needed for the HTML file are created first in the same 'baseURL' location - in my case the NSTemporaryDirectory()
- then run the following:
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)
}
})
推荐答案
我有同样的问题.但是我可以通过使用Swift的函数将html转换为属性文本,然后对属性文本使用UISimpleTextPrintFormatter来解决此问题.
I have the same problem. But I was able to get around it by using Swift's function to convert html to attributed text and then use UISimpleTextPrintFormatter with the attributed text.
我的原始代码:
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)上工作:
Working on Catalyst (and 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)
}
但是,与在iOS上相比,NSAttributedString(data:)似乎对您在Catalyst上抛出的内容更为敏感.例如,我是否对在iOS上正常工作的表有问题?因此,这不是一个完美的解决方案.
However, the NSAttributedString(data: ) seems to be more sensitive to what you throw at it on Catalyst than on iOS. For example, did I have problems with tables that worked fine on iOS. So it is not a perfect solution.
编辑似乎可以处理例如表格更好的是:
EDIT A better solution that seems to handle e.g. tables better is:
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)
这篇关于UIMarkupTextPrintFormatter和Mac Catalyst的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!