iOS含NSTextAttachment图片的NSAttributedString转PDF无图显示求助
解决UITextView富文本含NSTextAttachment转PDF图片不显示问题
我之前也踩过一模一样的坑:用UIPrintPageRenderer配合UITextView的viewPrintFormatter()生成PDF时,NSTextAttachment的图片只会留下空白占位,完全不渲染实际内容。这本质上是系统的UIPrintFormatter在处理富文本附件时,没有正确触发图片的绘制逻辑。下面给你两种经过验证的可行方案:
方法一:自定义UIPrintFormatter手动渲染富文本
我们可以自己实现一个UIPrintFormatter子类,借助CoreText的排版系统来绘制内容,这样能确保NSTextAttachment里的图片被正确渲染出来。
自定义PrintFormatter实现
class AttributedTextPrintFormatter: UIPrintFormatter { let attributedText: NSAttributedString private var textContainer: NSTextContainer! private var layoutManager: NSLayoutManager! private var textStorage: NSTextStorage! init(attributedText: NSAttributedString) { self.attributedText = attributedText super.init() setupTextLayoutSystem() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupTextLayoutSystem() { textStorage = NSTextStorage(attributedString: attributedText) layoutManager = NSLayoutManager() textContainer = NSTextContainer(size: .zero) textStorage.addLayoutManager(layoutManager) layoutManager.addTextContainer(textContainer) } override func rectForPage(at pageIndex: Int) -> CGRect { return printableRect } override func draw(in rect: CGRect, forPageAt pageIndex: Int) { let glyphRange = layoutManager.glyphRange(forPageAt: pageIndex, in: textContainer) layoutManager.drawGlyphs(forGlyphRange: glyphRange, at: rect.origin) } override func calculatePrintFormats(forPageRange range: NSRange, printRenderer renderer: UIPrintRenderer) { textContainer.size = renderer.printableRect.size let pageCount = layoutManager.numberOfPages(for: textContainer) self.pageRange = NSMakeRange(0, pageCount) } }
修改你的createPDF方法
把原来依赖的UISimpleTextPrintFormatter换成我们自定义的类:
public func createPDF(text: NSAttributedString, documentPath: URL, customPrintFormatter: UIPrintFormatter? = nil) { let printFormatter: UIPrintFormatter if let customFormatter = customPrintFormatter { printFormatter = customFormatter } else { // 使用自定义Formatter处理富文本附件 printFormatter = AttributedTextPrintFormatter(attributedText: text) } let renderer = UIPrintPageRenderer() renderer.addPrintFormatter(printFormatter, startingAtPageAt: 0) // A4尺寸设置 let pageSize = CGSize(width: 595.2, height: 841.8) let pageMargins = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72) let printableRect = CGRect(x: pageMargins.left, y: pageMargins.top, width: pageSize.width - pageMargins.left - pageMargins.right, height: pageSize.height - pageMargins.top - pageMargins.bottom) let paperRect = CGRect(x: 0, y: 0, width: pageSize.width, height: pageSize.height) renderer.setValue(NSValue(cgRect: paperRect), forKey: "paperRect") renderer.setValue(NSValue(cgRect: printableRect), forKey: "printableRect") let pdfData = NSMutableData() UIGraphicsBeginPDFContextToData(pdfData, paperRect, nil) renderer.prepare(forDrawingPages: NSMakeRange(0, renderer.numberOfPages)) let bounds = UIGraphicsGetPDFContextBounds() for i in 0 ..< renderer.numberOfPages { UIGraphicsBeginPDFPage() renderer.drawPage(at: i, in: bounds) } UIGraphicsEndPDFContext() do { try pdfData.write(to: documentPath) } catch { print(error.localizedDescription) } }
方法二:直接用CoreGraphics绘制到PDF上下文
如果觉得自定义Formatter太繁琐,也可以跳过UIPrintPageRenderer,直接将富文本逐页绘制到PDF上下文,这种方式更直接,也能保证图片正常显示:
public func createPDFDirectly(text: NSAttributedString, documentPath: URL) { // A4页面及边距设置 let pageSize = CGSize(width: 595.2, height: 841.8) let pageMargins = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72) let printableSize = CGSize(width: pageSize.width - pageMargins.left - pageMargins.right, height: pageSize.height - pageMargins.top - pageMargins.bottom) // 初始化CoreText排版组件 let textStorage = NSTextStorage(attributedString: text) let layoutManager = NSLayoutManager() let textContainer = NSTextContainer(size: printableSize) textStorage.addLayoutManager(layoutManager) layoutManager.addTextContainer(textContainer) let pageCount = layoutManager.numberOfPages(for: textContainer) let pdfData = NSMutableData() // 开始PDF上下文 UIGraphicsBeginPDFContextToData(pdfData, CGRect(origin: .zero, size: pageSize), nil) for pageIndex in 0..<pageCount { UIGraphicsBeginPDFPage() // 将坐标系平移到边距位置 UIGraphicsGetCurrentContext()?.translateBy(x: pageMargins.left, y: pageMargins.top) // 绘制当前页面的富文本内容 let glyphRange = layoutManager.glyphRange(forPageAt: pageIndex, in: textContainer) layoutManager.drawGlyphs(forGlyphRange: glyphRange, at: .zero) } UIGraphicsEndPDFContext() // 写入文件 do { try pdfData.write(to: documentPath) } catch { print(error.localizedDescription) } }
额外检查点
- 确认你的
NSTextAttachment的image不为nil:比如UIImage(named: "1.png")要确保图片存在于当前target的bundle中,或者用UIImage(contentsOfFile:)加载绝对路径的图片 - 如果你要转换的是UITextView的内容,直接传入
textView.attributedText即可,不需要依赖viewPrintFormatter()
内容的提问来源于stack exchange,提问作者Valerio Santinelli
相关产品推荐
相关产品推荐

