您必须自己在 pdf 中绘制文本以使其可编辑。
我稍微修改了您的代码,并且可以正常工作。您可以简单地复制/粘贴到新的应用视图控制器中以检查结果。
这是在 AffinityDesigner 中打开的文档的屏幕截图。蒙版正确应用为图层蒙版,并且文本可编辑。
它需要一些调整以尊重精确的布局。
import UIKit
class ViewController: UIViewController {
var labelFrame: CGRect { return CGRect(x: 0, y: 0, width: 200, height: 200 )}
lazy var label: UILabel = {
let label = UILabel(frame: labelFrame)
label.text = "Label Text"
label.font = UIFont.systemFont(ofSize: 25)
label.textAlignment = .center
label.textColor = UIColor.black
return label
}()
var maskImage = UIImage(named: "erasemask.png")
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(label)
let overlayView = UIImageView(frame: labelFrame)
overlayView.image = maskImage
label.mask = overlayView
exportAsPdfFromView()
}
func exportAsPdfFromView() {
let pdfPageFrame = CGRect(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Clip context
if let overlayImage = maskImage?.cgImage {
context.clip(to: labelFrame, mask: overlayImage)
}
// Draw String
let string: String = label.text ?? ""
let attributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.foregroundColor: label.textColor ?? .black,
NSAttributedString.Key.font: label.font ?? UIFont.systemFont(ofSize: 25)
]
let stringRectSize = string.size(withAttributes: attributes)
let x = (labelFrame.width - stringRectSize.width) / 2
let y = (labelFrame.height - stringRectSize.height) / 2
let stringRect = CGRect(origin: CGPoint(x: x, y: y), size: stringRectSize)
string.draw(in: stringRect, withAttributes: attributes)
UIGraphicsEndPDFContext()
saveViewPdf(data: pdfData)
}
func saveViewPdf(data: NSMutableData) {
guard let docDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
print(pdfPath)
data.write(to: pdfPath, atomically: true)
}
}
已编辑
作为比较,我使用 Apple view.render 函数添加了结果,使用了问题中的代码(蓝色背景,因此我们可以看到白色文本)。它清楚地表明此功能不支持可编辑文本和遮罩。
它将文档导出为一堆平面图像和额外的 - 无用的 - 组。
所以我想保留 pdf 实体类型的唯一解决方案是通过渲染每个对象来组成文档。
如果需要导出复杂的表单,制作一个在视图层次结构中爬行并渲染每个对象内容的助手一定不会太难。
如果您还需要使用交互式按钮呈现 pdf,您可能需要使用 Annotations(在 PDFKit 中提供)。顺便说一句,您还可以创建带有注释的可编辑字段,但我不确定它是否支持屏蔽。
最后编辑:
由于您使用外部框架来呈现 pdf ( PDFGenerator ),所以它是不同的。您只能覆盖框架使用的view.layer.render 函数并使用context.clip 函数。
要将 ui 组件导出为可编辑,我认为它们必须没有超级视图。一旦存在视图层次结构,就会创建一个支持位图,并且所有渲染调用都使用此位图作为参数,而不是用于调用第一个渲染的 PDFContext。
这就是 PDFGenerator 的工作原理,它在视图层次结构中爬行,通过从超级视图中移除视图、渲染到上下文并将它们移回层次结构中来呈现视图。
这样做的最大缺点是当视图在层次结构中移回时会导致错误。可能是因为约束丢失了,或者像 UIStackView 这样的视图表现不同。在他们的 github 上有许多未解决的问题。
我还不太明白为什么下面的标签即使在视图层次结构中也呈现为可编辑的。我想这是因为render 函数是由 Apple 完成的。现在不能再进一步了..
自定义字段示例:
遵守精确的字段布局可能需要更多的工作,但这是呈现为可编辑文本的视图的基础。它给出的结果与答案的第一部分完全相同。
- 它适用于任何遮罩视图,不仅是 UIImage。
- 即使在层次结构中也呈现为可编辑。
所以如果你在这个问题的初始代码中使用这个标签而不是 UILabel,它就可以工作。
// Extension to get mask view as an image
extension UIView {
var cgImage: CGImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, contentScaleFactor);
guard let context = UIGraphicsGetCurrentContext() else { return nil }
layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image?.cgImage
}
}
class PMLayer: CALayer {
// Needed to access text style
weak var label: UILabel?
override init() { super.init() }
required init?(coder: NSCoder) { super.init(coder: coder) }
override init(layer: Any) {
super.init(layer: layer)
if let pm = layer as? PMLayer {
label = pm.label
}
}
override func render(in ctx: CGContext) {
guard let label = label else { return }
// Clip context
if let mask = label.mask?.cgImage {
ctx.clip(to: label.frame, mask: mask)
}
// Draw String
let string: String = label.text ?? ""
let attributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.foregroundColor: label.textColor ?? .black,
NSAttributedString.Key.font: label.font ?? UIFont.systemFont(ofSize: 25)
]
let stringRectSize = string.size(withAttributes: attributes)
let x = (label.frame.width - stringRectSize.width) / 2
let y = (label.frame.height - stringRectSize.height) / 2
let stringRect = CGRect(origin: CGPoint(x: x, y: y), size: stringRectSize)
string.draw(in: stringRect, withAttributes: attributes)
}
}
class PMLabel: UILabel {
override class var layerClass: AnyClass { return PMLayer.self }
// Be sure the label is correctly linked to the layer when we access it
override var layer: CALayer {
(super.layer as? PMLayer)?.label = self
return super.layer
}
}