让用户将PDF外部应用保存在IOS Swift中

时间:2018-09-20 16:56:15

标签: swift file pdf save user-permissions

我将PDF保存为我的应用程序,但我希望让用户将PDF文档保存在应用程序之外的目录中。 (对不起,英语不好,我来自瑞士。)

格式化程序中的markupText是否有一些重要的内容,我必须将其用于某些内容?

我认为-UIGraphicsBeginPDFContextToFile(pathForPDF,rect,nil)-将文件保存在我的应用程序的文档目录中,是否正确?

其他问题。我的代码中有任何错误。我从这里的答案中提取了很多代码,希望即时通讯正确无误。

import UIKit 
import Foundation

class PdfErstellung {
    static func PdfErstellen(_ auswahlZeilen : [LebensmittelDataTV], _ vitalstoffWerteListe : [LebensmittelDataTV], _ heuteString : String) {
        var html = "<html><head><meta charset='UTF-8'></head><body>Erstelle Vitalstoffwerte PDF</body></html"

        // 1. Create Print Formatter with input text.
        let formatter = UIMarkupTextPrintFormatter(markupText: html)

        // 2. Add formatter with pageRender
        let render = UIPrintPageRenderer()
        render.addPrintFormatter(formatter, startingAtPageAt: 0)

        // 3. Assign paperRect and printableRect

        let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
        let printable = page.insetBy(dx: 0, dy: 0)
        let rect = CGRect.zero

        render.setValue(NSValue(cgRect: page), forKey: "paperRect")
        render.setValue(NSValue(cgRect: printable), forKey: "printableRect")

        // 4. Create PDF context and draw
        let fileName = "Vitalstoffwerte " + heuteString
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let documentsDirectory = paths[0]
        let pathForPDF = documentsDirectory.appendingFormat("/" + fileName)

        UIGraphicsBeginPDFContextToFile(pathForPDF, rect, nil)

        // Start Draw of page

        var y = 0

        var stringRechteckX50 = CGRect(x: 50, y: y, width: 100, height: 20)
        var stringRechteckX160 = CGRect(x: 160, y: y, width: 30, height: 20)
        var stringRechteckX170 = CGRect(x: 170, y: y, width: 10, height: 20)
        var stringRechteckX220 = CGRect(x: 220, y: y, width: 100, height: 20)
        var stringRechteckX330 = CGRect(x: 330, y: y, width: 30, height: 20)
        var stringRechteckX340 = CGRect(x: 340, y: y, width: 10, height: 20)

        var paragraphStyle = NSMutableParagraphStyle()

        var font = UIFont(name: "Helvetica Bold", size: 20.0)
        var text = ""

        let attributes = [
            NSAttributedString.Key.paragraphStyle: paragraphStyle,
            NSAttributedString.Key.font: font,
            NSAttributedString.Key.foregroundColor: UIColor.black
        ]

        var attributedString = NSAttributedString(string: text, attributes: attributes)

        // ÜBERSCHRIFT

        font = UIFont(name: "Helvetica Bold", size: 20.0)
        text = "Tagesbedarf in % vom " + heuteString
        paragraphStyle.alignment = .left
        var stringRechteck = CGRect(x: 50, y: 50, width: 300, height: 40)
        attributedString.draw(in: stringRechteck)


        // Vitalstoffwerte Liste

        y = 80

        for zeile in 0..<vitalstoffWerteListe.count {

            let druckenVitalstoffWerte = vitalstoffWerteListe[zeile]

            if druckenVitalstoffWerte.zeilenInfoID == 0 { // Überschrift

                y = y + 10
                font = UIFont(name: "Helvetica Bold", size: 14.0)
                text = druckenVitalstoffWerte.name
                attributedString.draw(in: stringRechteckX50)

            } else { // Detail

                font = UIFont(name: "Helvetica", size: 12.0)
                text = druckenVitalstoffWerte.name
                attributedString.draw(in: stringRechteckX50)

                text = druckenVitalstoffWerte.anzahl
                paragraphStyle.alignment = .right
                attributedString.draw(in: stringRechteckX160)

                text = druckenVitalstoffWerte.masse
                paragraphStyle.alignment = .left
                attributedString.draw(in: stringRechteckX170)

            }
            y = y + 30
        }

        y = 80
        text = "Lebensmittel"
        font = UIFont(name: "Helvetica Bold", size: 14.0)
        paragraphStyle.alignment = .left
        attributedString.draw(in: stringRechteckX220)


        // Lebensmittelliste

        y = 120

        for zeile in 0..<auswahlZeilen.count {

            let auswahlZeilenObjekt = auswahlZeilen[zeile]

            font = UIFont(name: "Helvetica", size: 12.0)
            text = auswahlZeilenObjekt.name
            attributedString.draw(in: stringRechteckX220)

            text = auswahlZeilenObjekt.anzahl
            paragraphStyle.alignment = .right
            attributedString.draw(in: stringRechteckX330)

            text = auswahlZeilenObjekt.masse
            paragraphStyle.alignment = .left
            attributedString.draw(in: stringRechteckX340)

        }
        y = y + 30

         // End Draw of page

        UIGraphicsEndPDFContext()
    }


    // I think the Pdf was saved before, but i do not know exactly
    static func savePdf(_ urlString: String, _ fileName: String) {
        DispatchQueue.main.async {
            let url = URL(string: urlString)
            let pdfData = try? Data.init(contentsOf: url!)
            let resourceDocPath = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
            let pdfNameFromUrl = "YourAppName-\(fileName).pdf"
            let actualPath = resourceDocPath.appendingPathComponent(pdfNameFromUrl)
            do {
                try pdfData?.write(to: actualPath, options: .atomic)
                print("pdf successfully saved!")
            } catch {
                print("Pdf could not be saved")
            }
        }
    }
}

----编辑-----

我有很多改变。感谢您提供的链接,我找到了其他链接并有了新的想法。

我的下一个问题是:

Vitalstoffcontroller [6161:165020] [默认] [错误]无法确定URL / Users / lukashedinger / Library / Developer / CoreSimulator / Devices / 4DE0F1A6-67B7-4909-8C46-78278D403E63 /数据/容器/数据/应用程序/ D72F4EE0-D330-4B2D-924E-E17FC805EF55 / tmp / Vitalstoffwerte 21092018-21092018.pdf(n)由文件提供商管理

我发现了这个,但不明白我的错误是什么。

UIActivityViewController Error: Failed to determine whether URL is managed by a file provider

How to save file in the documents folder?

(也许是IOS 12的错误,我读过:https://forums.developer.apple.com/thread/103198

我的新密码

    // 5. Save PDF file

    let dateiName = "Vitalstoffwerte " + heuteString

    let path = "\(NSTemporaryDirectory())\(dateiName).pdf"



    pdfData.write( toFile: path, atomically: true)

    print("open \(path)")


    // var fileUrl: URL = URL(fileURLWithPath: NSTemporaryDirectory())
    // fileUrl.appendPathComponent("foo")
    // fileUrl.appendPathExtension("bar")


    let fileUrl: URL = URL(fileURLWithPath: path)

    let directory = "\(NSTemporaryDirectory())"

    let directoryUrl : URL = URL(fileURLWithPath: directory)

    print("open \(fileUrl)")
    print("open \(directoryUrl)")

    let fm = FileManager.default

    //let fileName = String((fileUrl.lastPathComponent)) as NSString
    //let documentsUrl:URL =  fm.urls(for: .documentDirectory, in: .userDomainMask).first!
    //let destinationFileUrl = documentsUrl.appendingPathComponent("\(fileName)")

    //print("open \(documentsUrl)")


    //do {
    //    try fm.removeItem(at: destinationFileUrl)
    //} catch (let deleteError) {
    //    print("Error deleting a file \(destinationFileUrl) : \(deleteError)")
    //}

    //do {
    //    try FileManager.default.copyItem(at: fileUrl, to: destinationFileUrl)
    //} catch (let writeError) {
    //    print("Error creating a file \(destinationFileUrl) : \(writeError)")
    //}

    do {

        //Show UIActivityViewController to save the downloaded file

        let contents  = try fm.contentsOfDirectory(at: directoryUrl, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)

        print("open \(contents)")

        //for i in 0..<contents.count {

            let activityViewController = UIActivityViewController(activityItems: [contents[0]], applicationActivities: nil)
            self.present(activityViewController, animated: true, completion: nil)

                // for IPAD
                if let popOver = activityViewController.popoverPresentationController {
                    popOver.sourceView = self.view
                    //popOver.sourceRect =
                    //popOver.barButtonItem
                }

        //}
        // return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]lastObject];
    } catch {
        print("Fehler")
    }
}

-------更新:找到了解决我的问题的方法,但是没有解决文件-----

我将pdfData直接提供给ViewController,并且不将其保存到文件中。

UIActivityViewController(activityItems:[pdfData],applicationActivities:nil)

我不使用文件,这样对我来说已经足够

1 个答案:

答案 0 :(得分:-1)

我完成了,那就是我的代码

func SavePdfUser(){

    // Create page rect
    let pageRect = CGRect(x: 0, y: 0, width: 595.28, height: 841.89) // A4, 72 dpi

    // Create PDF context and draw
    let pdfData = NSMutableData()

    UIGraphicsBeginPDFContextToData(pdfData, pageRect, nil)
    UIGraphicsBeginPDFPage()

    // From here you can draw page, best make it in a function
    PdfErstellung.PdfErstellen(auswahlZeilen, vitalstoffWerteListe, heuteString)

    UIGraphicsEndPDFContext()

    // Save pdf DATA through user
    let activityViewController = UIActivityViewController(activityItems: [pdfData], applicationActivities: nil)
    activityViewController.popoverPresentationController?.sourceView = self.view // für IPAD nötig
    self.present(activityViewController, animated: true, completion: nil)

    // save temporary for preview 
    let dateiName = "Vitalstoffwerte " + heuteString
    let fileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(dateiName).pdf")

    do {
        try pdfData.write(to: fileUrl, options: .atomic)
    } catch {
        print(error,"file not save")
    }

    // Preview
    let webview = WKWebView(frame: UIScreen.main.bounds)
    view.addSubview(webview)
    webview.load(URLRequest(url: fileUrl))
}

导入UIKit 导入基金会

PdfErstellung类{

static func PdfErstellen(_ auswahlZeilen : [LebensmittelDataTV], _ vitalstoffWerteListe : [LebensmittelDataTV], _ heuteString : String) {

    var y = 0 // Points from above - von oben
    var x = 0 // Points form left - von links
    var width = 0 // length of rect - länge vom rechteck
    var height = 0 // height of rect - höhe vom rechteck
    var stringRechteck = CGRect(x: x, y: y, width: width, height: height) // make rect for text - rechteck für Text

    var paragraphStyle = NSMutableParagraphStyle() // text alignment - text ausrichtung

    var font = UIFont(name: "HelveticaNeue-Bold", size: 10.0) // Important: the font name must be written correct - Wichtig: Textname muss korrekt geschrieben werden
    var text = ""

    let attributes = [
        NSAttributedString.Key.paragraphStyle: paragraphStyle,
        NSAttributedString.Key.font: font,
        NSAttributedString.Key.foregroundColor: UIColor.black
    ]

    // Title - ÜBERSCHRIFT

    font = UIFont(name: "HelveticaNeue-Bold", size: 20.0)
    text = "Tagesbedarf in % vom " + heuteString // heuteString = date/datum
    paragraphStyle.alignment = .left
    x = 50; y = 30; width = 300; height = 40
    stringRechteck = CGRect(x: x, y: y, width: width, height: height)
    text.draw(in: stringRechteck, withAttributes: attributes)


    // List - Vitalstoffwerte Liste

    y = 45

    for zeile in 0..<vitalstoffWerteListe.count {

        let druckenVitalstoffWerte = vitalstoffWerteListe[zeile]

        if druckenVitalstoffWerte.zeilenInfoID == 0 { // Title - Überschrift

            y = y + 5 // distance to above becaus is title - Abstand nach oben, weil Überschrift
            x = 50; width = 80; height = 20
            stringRechteck = CGRect(x: x, y: y, width: width, height: height)
            font = UIFont(name: "HelveticaNeue-Bold", size: 10.0) // change font - verändere schrift
            text = druckenVitalstoffWerte.name
            text.draw(in: stringRechteck, withAttributes: attributes)
            font = UIFont(name: "HelveticaNeue", size: 8.0) // change font back - schrift zurück ändern

        } else { // or detail

            x = 50; width = 90; height = 20
            stringRechteck = CGRect(x: x, y: y, width: width, height: height)
            text = druckenVitalstoffWerte.name
            text.draw(in: stringRechteck, withAttributes: attributes)

            x = 150; width = 40
            stringRechteck = CGRect(x: x, y: y, width: width, height: height)
            text = druckenVitalstoffWerte.anzahl
            paragraphStyle.alignment = .right
            text.draw(in: stringRechteck, withAttributes: attributes)
            paragraphStyle.alignment = .left

            x = 195; width = 20
            stringRechteck = CGRect(x: x, y: y, width: width, height: height)
            text = druckenVitalstoffWerte.masse
            text.draw(in: stringRechteck, withAttributes: attributes)

        }
        y = y + 10 // 10 down for next line - 10 nach unten für nächste Zeile
    }

    // Title - Überschrift

    x = 220; y = 50; width = 100; height = 20
    text = "Lebensmittel"
    font = UIFont(name: "HelveticaNeue-Bold", size: 8.0)
    paragraphStyle.alignment = .left
    stringRechteck = CGRect(x: x, y: y, width: width, height: height)
    text.draw(in: stringRechteck, withAttributes: attributes)
    font = UIFont(name: "HelveticaNeue", size: 8.0)

    // List - Lebensmittelliste

    y = 60

    for zeile in 0..<auswahlZeilen.count {

        let auswahlZeilenObjekt = auswahlZeilen[zeile]

        x = 220; width = 90; height = 20
        stringRechteck = CGRect(x: x, y: y, width: width, height: height)
        text = auswahlZeilenObjekt.name
        text.draw(in: stringRechteck, withAttributes: attributes)

        x = 320; width = 40
        stringRechteck = CGRect(x: x, y: y, width: width, height: height)
        text = auswahlZeilenObjekt.anzahl
        paragraphStyle.alignment = .right
        text.draw(in: stringRechteck, withAttributes: attributes)
        paragraphStyle.alignment = .left

        x = 365; width = 20
        stringRechteck = CGRect(x: x, y: y, width: width, height: height)
        text = auswahlZeilenObjekt.masse
        text.draw(in: stringRechteck, withAttributes: attributes)

        y = y + 10

        if zeile == 75 { // 76 items - Zeilen
            break
        }
    }

    y = 60

     if auswahlZeilen.count > 75 { // new

        for zeile in 76..<auswahlZeilen.count {

            let auswahlZeilenObjekt = auswahlZeilen[zeile]

            x = 390; width = 90; height = 20
            stringRechteck = CGRect(x: x, y: y, width: width, height: height)
            text = auswahlZeilenObjekt.name
            text.draw(in: stringRechteck, withAttributes: attributes)

            x = 490; width = 40
            stringRechteck = CGRect(x: x, y: y, width: width, height: height)
            text = auswahlZeilenObjekt.anzahl
            paragraphStyle.alignment = .right
            text.draw(in: stringRechteck, withAttributes: attributes)
            paragraphStyle.alignment = .left

            x = 535; width = 20
            stringRechteck = CGRect(x: x, y: y, width: width, height: height)
            text = auswahlZeilenObjekt.masse
            text.draw(in: stringRechteck, withAttributes: attributes)

            y = y + 10

            if zeile == 151 { // 76 items - Zeilen
                break
            }
        }
    }
}

}

-编辑-仅在iOS 12中才是问题专家。

我不知道为什么,但是在某些模拟器中:我可以制作文件,并将URL赋予UIActivityViewController,并且我拥有自己的文件名,而其他URL则失败,我必须直接给pdfData。

我的想法:我先尝试使用URL,如果失败,则提供pdfData

do {
        let dateiName = "Vitalstoffwerte " + heuteString
        let fileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(dateiName).pdf")

        do {
            try pdfData.write(to: fileUrl, options: .atomic)
        } catch {
            print(error,"file not save")
        }

        let activityViewController = UIActivityViewController(activityItems: [fileUrl], applicationActivities: nil)
        activityViewController.popoverPresentationController?.sourceView = self.view // für IPAD nötig
        self.present(activityViewController, animated: true, completion: nil)

    } catch {

        let activityViewController = UIActivityViewController(activityItems: [pdfData], applicationActivities: nil)
        activityViewController.popoverPresentationController?.sourceView = self.view // für IPAD nötig
        self.present(activityViewController, animated: true, completion: nil)
    }

我的问题专家,如何找出用户是否可以保存文件(如果尝试过),是否可以在日志中询问错误代码?该做,抓不走。

2018-09-25 15:36:55.787894 + 0200 Vitalstoffcontroller [8656:186334] [默认] [错误]无法确定URL / Users / lukashedinger / Library / Developer / CoreSimulator / Devices / 45A185C5-6DEA-4A52 -B415-939758FB7F7E / data / Containers / Data / Application / 2469BE4A-53B2-4545-B5D7-E64FBCDB383E / tmp / Vitalstoffwerte 25092018-25092018.pdf(n)由文件提供商管理 2018-09-25 15:36:56.312499 + 0200 Vitalstoffcontroller [8656:186334] [ShareSheet]已取消请求-错误:操作无法完成。参数无效

-编辑-

一个临时解决方案是,在ios12中,我将数据提供给UIActivityViewController,否则我将数据另存为文件,并将URL提供给UIActivityViewController。

     if #available(iOS 12.0, *) {

        let activityViewController = UIActivityViewController(activityItems: [pdfData], applicationActivities: nil)
        activityViewController.popoverPresentationController?.sourceView = self.view // für IPAD nötig
        self.present(activityViewController, animated: true, completion: nil)

    } else {

        let dateiName = "Vitalstoffwerte " + heuteString
        let fileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(dateiName).pdf")
        do {
            try pdfData.write(to: fileUrl, options: .atomic)
        } catch {
            print(error,"file not save")
        }
        let activityViewController = UIActivityViewController(activityItems: [fileUrl], applicationActivities: nil)
        activityViewController.popoverPresentationController?.sourceView = self.view // für IPAD nötig
        self.present(activityViewController, animated: true, completion: nil)
    }