Ios 从UILabel文本掩码获取PDF

Ios 从UILabel文本掩码获取PDF,ios,swift,xcode,uilabel,mask,Ios,Swift,Xcode,Uilabel,Mask,我正在尝试使用UILabel文本掩码从UIView获取PDF override func viewDidLoad() { super.viewDidLoad() let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200 )) label.text = "Label Text" label.font = UIFont.system

我正在尝试使用UILabel文本掩码从UIView获取PDF

  override func viewDidLoad() {
        super.viewDidLoad()
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
        label.text = "Label Text"
        label.font = UIFont.systemFont(ofSize: 25)
        label.textAlignment = .center
        label.textColor = UIColor.white

        let overlayView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
        overlayView.image = UIImage(named: "erasemask.png")
        
        label.mask = overlayView
        view_process.addSubview(label)
        
    }

   func exportAsPdfFromView(){

        let pdfPageFrame = CGRect(x: 0, y: 0, width: view_process.bounds.size.width, height: view_process.bounds.size.height)

        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
        UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)

        guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
       
        view_process.layer.render(in: pdfContext)
        UIGraphicsEndPDFContext()
          
       let path = self.saveViewPdf(data: pdfData)
        print(path)

      }

    func saveViewPdf(data: NSMutableData) -> String {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let docDirectoryPath = paths[0]
        let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
        if data.write(to: pdfPath, atomically: true) {
            return pdfPath.path
        } else {
            return ""
        }
      }
但是我没有得到带掩码的PDF。我不想将UIView转换为UImage,然后再将UImage转换为PDF我想编辑PDF,所以不想转换为UIImage。

有人能帮我把蒙面UILabel转换成PDF吗


这里是erasemask.png

您必须自己在pdf中绘制文本,使其可编辑

我稍微修改了你的代码,它可以工作。您只需复制/粘贴一个新的应用程序视图控制器即可检查结果

这是在AffinityDesigner中打开的文档的屏幕截图。遮罩被正确应用为图层遮罩,并且文本是可编辑的

它需要一些调整,以尊重准确的布局

导入UIKit
类ViewController:UIViewController{
var labelFrame:CGRect{return CGRect(x:0,y:0,width:200,height:200)}
惰性变量标签:UILabel={
let label=UILabel(框架:labelFrame)
label.text=“label text”
label.font=UIFont.systemFont(大小:25)
label.textAlignment=.center
label.textColor=UIColor.black
退货标签
}()
var maskImage=UIImage(名为:“erasemask.png”)
重写func viewDidLoad(){
super.viewDidLoad()
view.addSubview(标签)
让overlayView=UIImageView(帧:labelFrame)
overlayView.image=maskImage
label.mask=覆盖视图
exportAsPdfFromView()
}
func exportAsPdfFromView(){
让pdfPageFrame=CGRect(x:0,y:0,width:view.bounds.size.width,height:view.bounds.size.height)
设pdfData=NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData,pdfPageFrame,无)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame,无)
guard let context=UIGraphicsGetCurrentContext()else{return}
//剪辑上下文
如果let overlayImage=maskImage?.cgImage{
context.clip(到:labelFrame,掩码:overlayImage)
}
//拉线
let string:string=label.text??“”
let属性:[NSAttributedString.Key:任意]=[
NSAttributedString.Key.foregroundColor:label.textColor???.black,
NSAttributedString.Key.font:label.font??UIFont.systemFont(大小:25)
]
让stringRectSize=string.size(withAttributes:attributes)
设x=(labelFrame.width-stringRectSize.width)/2
设y=(labelFrame.height-stringRectSize.height)/2
设stringRect=CGRect(原点:CGPoint(x:x,y:y),大小:stringRectSize)
draw(in:stringRect,withAttributes:attributes)
UIGraphicsSendPdfContext()
saveViewPdf(数据:pdfData)
}
func saveViewPdf(数据:NSMutableData){
guard let docDirectoryPath=FileManager.default.URL(用于:.documentDirectory,位于:.userDomainMask中)。首先是{return}
让pdfPath=docDirectoryPath.appendingPathComponent(“viewPdf.pdf”)
打印(pdfPath)
data.write(to:pdfPath,原子性:true)
}
}
编辑

作为比较,我使用Apple
view.render
函数添加了结果,使用问题中的代码(蓝色背景,以便我们可以看到白色文本)。它清楚地表明该函数不支持可编辑文本和掩蔽。 它将文档导出为一堆平面图像和多余的无用组。 所以我想保持pdf实体类型的唯一解决方案是通过呈现每个对象来合成文档

如果需要导出复杂表单,则创建在视图层次结构中爬行并呈现每个对象内容的辅助对象一定不会太难

如果还需要使用交互式按钮呈现pdf,则可能需要使用注释(在PDFKit中提供)。顺便说一下,您还可以创建带有注释的可编辑字段,但我不确定它是否支持屏蔽

上次编辑:

因为您使用外部框架来呈现pdf(PDFGenerator),所以它是不同的。您只能覆盖框架使用的
view.layer.render
函数,并使用
context.clip
函数

要将ui组件导出为可编辑的,我认为它们必须没有superview。一旦存在视图层次结构,就会创建一个备份位图,并且所有渲染调用都以该位图作为参数,而不是用于调用第一个渲染的PDFContext

PDFGGenerator就是这样工作的,它通过从superview中删除渲染视图、渲染到上下文并在层次中将它们移回,在视图层次中爬行渲染视图

这样做的最大缺点是,当视图在层次结构中向后移动时,会导致错误。可能是因为约束丢失,或者某些视图(如UIStackView)的行为不同。在他们的github上有许多关于这一点的公开问题

我还不太明白为什么下面的标签即使在视图层次结构中也呈现为可编辑的。我想这是由于苹果公司的
render
功能造成的。现在不能再谈这个了

自定义字段示例:

可能需要进行更多的工作才能实现精确的字段布局,但这是作为可编辑文本呈现的视图的基础。它给出了与答案第一部分完全相同的结果

  • 它适用于任何遮罩视图,而不仅仅是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
    }
}
这是我的决定

import UIKit
import PDFKit

class ViewController: UIViewController {
    
    @IBOutlet weak var view_process: UIView!
    @IBOutlet weak var pdf_view: UIView!
    let documentInteractionController = UIDocumentInteractionController()
    @IBAction func bClick(_ sender: Any) {
        exportAsPdfFromView()
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        documentInteractionController.delegate = self
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
        label.text = "Label Text"
        label.font = UIFont.systemFont(ofSize: 25)
        label.textAlignment = .center
        label.textColor = UIColor.blue
        
        let overlayView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
        overlayView.image = UIImage(named: "erasemask.png")
        
        label.mask = overlayView
        view_process.addSubview(label)
        
    }
    
    func exportAsPdfFromView(){
        
        let pdfPageFrame = view_process.bounds//.CGRect(x: 0, y: 0, width: view_process.bounds.size.width, height: view_process.bounds.size.height)
        
        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
        UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
        
        let pdfContext = UIGraphicsGetCurrentContext()!
        
        view_process.layer.render(in: pdfContext)
        UIGraphicsEndPDFContext()
        
        let path = self.saveViewPdf(data: pdfData)
        print(path)
        self.share(url: URL(string: "file://\(path)")!)
    }
    
    func saveViewPdf(data: NSMutableData) -> String {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let docDirectoryPath = paths[0]
        let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
        if data.write(to: pdfPath, atomically: true) {
            return pdfPath.path
        } else {
            return ""
        }
    }
    
    func openPdf(path: String) {
        let pdfView = PDFView(frame: self.view.bounds)
        pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        pdf_view.addSubview(pdfView)
        
        // Fit content in PDFView.
        pdfView.autoScales = true
        
        // Load Sample.pdf file from app bundle.
        let fileURL = URL(string: path)
        pdfView.document = PDFDocument(url: fileURL!)
    }
    
    
    func share(url: URL) {
        documentInteractionController.url = url
        //        documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
        //        documentInteractionController.name = url.localizedName ?? url.lastPathComponent
        documentInteractionController.presentPreview(animated: true)
    }
}

extension ViewController: UIDocumentInteractionControllerDelegate {
    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
        guard let navVC = self.navigationController else {
            return self
        }
        return navVC
    }
}

是否希望蒙面标签在PDF中显示为文本?PDF能处理这个问题吗?@paiv你明白我的意思了吗?请添加一些背景色来查看过程,这样你就可以看到我们没有得到我们想要的结果。你可以显示,可能是一张图片,你想要什么?你检查了我们保存的PDF吗?我