разрешить пользователю сохранять PDF вне приложения в IOS Swift - PullRequest
0 голосов
/ 20 сентября 2018

Я сделал PDF-файл и сохраню его в своем приложении, но я хочу, чтобы пользователь сохранил PDF-документ в каталоге за пределами моего приложения.(Извините за плохой английский, я из Швейцарии.)

Текст разметки в форматере, это что-то важное, что я должен использовать для чего-то?

Я думаю - 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/ Устройства / 4DE0F1A6-67B7-4909-8C46-78278D403E63 / данные / контейнеры / данные / приложение / D72F4EE0-D330-4B2D-924E-E17FC805EF55 / tmp / Vitalstoffwerte 21092018-21092018.pdf (n) управляется файлом *1018*провайдер

Я нашел это, но не понимаю, в чем моя ошибка.

Ошибка UIActivityViewController: не удалось определить, управляется ли URL-адрес поставщиком файлов

Как сохранить файл в папке документов?

(Возможно, это ошибка 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 голосов
/ 23 сентября 2018

Я закончу, это мой код

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

import UIKit import Foundation

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.

Не знаю почему, но в каком-то симуляторе: я могу создать файл и дать 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)
    }

Моя проблема в том, как я могу узнать, пользователь мог сохранить файл или нет(если бы он попробовал), могу ли я спросить после кода ошибки в журнале?do, catch не отправляются.

2018-09-25 15: 36: 55.787894 + 0200 Vitalstoffcontroller [8656: 186334] [по умолчанию] [ОШИБКА] Не удалось определить, является ли URL / Users / lukashedinger / Library /Разработчик / CoreSimulator / Устройства / 45A185C5-6DEA-4A52-B415-939758FB7F7E / данные / контейнеры / данные / приложение / 2469BE4A-53B2-4545-B5D7-E64FBCDB383E / tmp / Vitalstoffwerte 25092018-25092018.pdf управляется файлом (файл v15082018.pdf)провайдер 2018-09-25 15: 36: 56.312499 + 0200 Vitalstoffcontroller [8656: 186334] [ShareSheet] отменил запрос - ошибка: операция не может быть завершена.Недопустимый аргумент

- РЕДАКТИРОВАТЬ -

ma временное решение - в 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)
    }
...