让用户在IOS swift中将pdf保存在应用程序之外 [英] let user save pdf outside app in IOS swift

查看:28
本文介绍了让用户在IOS swift中将pdf保存在应用程序之外的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了一个 PDF 并将其保存在我的应用程序中,但我想让用户将 PDF 文档保存在我的应用程序外部的目录中.(抱歉英语不好,我来自瑞士.)

I Made a PDF an save it in my App, but I want let de user save the PDF document in a directory outside of my app. (Sorry for bad English, I am from Swiss.)

格式化程序中的标记文本,这是不是很重要,我必须使用它?

The markupText in the formatter, ist this something important, that I must use for something?

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

I think - UIGraphicsBeginPDFContextToFile(pathForPDF, rect, nil) - save the file in the document directory of my app, ist this correct?

其他问题.我的代码中是否有任何错误.我从这里的答案中提取了很多代码,希望我能做对.

Other question. Ist there any mistake in my code. I have taken a lot of code from answers here and hope that im make it right.

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")
            }
        }
    }
}

---- 编辑 -----

---- Edit -----

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

I have change a lot. Thanks for the link, I found other links and had new ideas.

我的下一个问题是:

Vitalstoffcontroller[6161:165020] [default] [ERROR] 无法确定URL/Users/lukashedinger/Library/Developer/CoreSimulator/Devices/4DE0F1A6-67B7-4909-8C46-78278D403E63/data/Containers/Data/Application/D72F4EE0-D330-4B2D-924E-E17FC805EF55/tmp/Vitalstoffwerte 21092018-21092018.pdf (n) 由文件提供商管理

Vitalstoffcontroller[6161:165020] [default] [ERROR] Failed to determine whether URL /Users/lukashedinger/Library/Developer/CoreSimulator/Devices/4DE0F1A6-67B7-4909-8C46-78278D403E63/data/Containers/Data/Application/D72F4EE0-D330-4B2D-924E-E17FC805EF55/tmp/Vitalstoffwerte 21092018-21092018.pdf (n) is managed by a file provider

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

I found this, but not understand, what my mistake is.

UIActivityViewController 错误:无法确定是否URL 由文件提供者管理

如何将文件保存在文档文件夹中?

(也许是我阅读的 IOS 12 的一个错误:https://forums.developer.apple.com/thread/103198)

(Perhaps is a bug of IOS 12 I read: 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")
    }
}

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

------- UPDATE : Found a way for my problem, but not for file -----

我将 pdfData 直接提供给 ViewController,而不是将其保存到文件中.

I give the pdfData direct to ViewController, and do not save it to file.

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

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

我不使用文件,这种方式对我来说已经足够了

I not use a file, that way is good enough for me

推荐答案

我写完了,这就是我的代码

I am finish, that is my code

func SavePdfUser(){

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导入基金会

import UIKit import Foundation

类 PdfErstellung {

class 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 中.

-- EDIT -- The Problem ist only in IOS 12.

我不知道为什么,但在一些模拟器中:我可以制作文件,并将 URL 提供给 UIActivityViewController,我有自己的文件名,而在其他 URL 失败,我必须直接提供 pdfData.

I do not know why, but in some simulator: I can make the file, and give the URL to UIActivityViewController and I have my own filename and in other the URL fail, and I must give the pdfData directly.

我的想法:我先尝试 URL,如果失败,然后我给出 pdfData

My idea: I Try first the URL and if this fail then I give the 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)
    }

我的问题是,我怎么知道,用户是否可以保存文件(如果他尝试过),我可以问日志中的错误代码后面的内容吗?做,抓不走.

My problem ist, how I can find out, the user could save the file or not (if he had try it), can I ask the after the error code in the log? the do, catch do not go.

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] 取消请求 - 错误:操作无法完成.无效参数

2018-09-25 15:36:55.787894+0200 Vitalstoffcontroller[8656:186334] [default] [ERROR] Failed to determine whether 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) is managed by a file provider 2018-09-25 15:36:56.312499+0200 Vitalstoffcontroller[8656:186334] [ShareSheet] cancelled request - error: The operation couldn’t be completed. Invalid argument

-- 编辑--

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

ma temporary solution is, in ios12 I give the data to UIActivityViewController , otherwise I save the data as file and give the url to 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)
    }

这篇关于让用户在IOS swift中将pdf保存在应用程序之外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆