LoginSignup
7
7

More than 3 years have passed since last update.

iOSアプリのHTMLテンプレートに基づいてPDFを作成します(例えばレシート)

Last updated at Posted at 2020-08-12

「大好きなお弁当屋さんのレシートアプリを作ろう!」

この例では、お客さんの購入した品目を入力し、PDFレシートを生成できるレシートアプリを作ります。PDFレシートはHTMLテンプレートに基づいて生成されます。

image.png

image.png

この記事で学ぶこと:

  • 表を含むウェブページのコーディング(簡単)
  • PDFファイルをHTMLの文字列から生成
  • PDFのプレビューウィンドウを表示

スタータープロジェクト

ここでスタータープロジェクトをダウンロードする必要があります。これは、顧客のレシートにアイテムを入力できるミニ販売管理ソフトウェアです。

完成したプロジェクトはこちらで見ることができます。

HTML文字列の準備

PDFはWebページから生成されるため、いくつかのHTMLコードを記述する必要があります。

HTMLヘッダー

headerHTMLstring というヘルパー関数を追加できます。 htmlファイルのヘッダーには、Webページのタイトル、テーブルのスタイル、およびテーブルの最初の行(ヘッダー)があります。

func headerHTMLstring() -> String {
    //htmlヘッダーを生成します。
    //たとえば、ここに店の名前を入力できます
    return """
    <!DOCTYPE html>
    <html>
        <head>
                <title>レシート</title>
        <style>
                table, th, td {
                  border: 1px solid black;
                  border-collapse: collapse;
                }
        </style>
        <body>
            <h2>Invoice</h2>
            <table style="width:100%">
                <tr>
                    <th>名前</th>
                    <th>価格</th>
                </tr>
    """
}

もちろん、レシートをかっこよく見せるために、そこにあなたの店舗の写真を追加することができます。または、レシートの番号や購入時刻を追加することもできます。

各販売アイテムの個別の行

ここで、表内で1つの行を表すHTMLコードを生成する関数を追加します。その行には、販売アイテムの名前とその価格が含まれています。

func getSingleRow(itemName: String, itemPrice: Int) -> String {
    return """
    <tr>
        <td>\(itemName)</td>
        <td>\(String(itemPrice))</td>
    </tr>
    """
}

HTMLフッター

ついにHTMLフッターを追加

func footerHTMLstring() -> String {
    return """
        </table>

        </body>
    </html>
    """
}

HTML文字列を準備

今や私たちは、ヘッダー、列、フッターを組み合わせてHTML文字列を作成します

私たちは完成したHTML文字列を蓄積する変数からスタートします。

var htmlString = ""

HTMLのヘッダーの内容を変数に追加します。

let htmlHeader = headerHTMLstring()
htmlString.append(htmlHeader)

セール品を追加するために商品配列にループ処理を行ってください

var totalPrice = 0
for item in items {
    let name = item.name
    let price = item.price
    let rowString = getSingleRow(itemName: name, itemPrice: price)
    htmlString.append(rowString)
    totalPrice += price
}

すると合計金額とHTMLファイルのフッターを追加できます

// 合計金額を追加する
htmlString.append("\n 合計金額: \(totalPrice) yen \n")
// フッターを取得する
let footerString = footerHTMLstring()
htmlString.append(footerString)

これでHTMLのWebページのコードが完成したことになります。PDFファイルの生成作業をしていきましょう。

HTML文字列からPDFファイルを作成しましょう

ここでは UIPrintPageRenderer , UIMarkupTextPrintFormatter, UIGraphics を使います

まずレンダラーの設定を行い、希望の紙のサイズを指定します

let renderer = UIPrintPageRenderer()
let paperSize = CGSize(width: 595.2, height: 841.8) //B6
let paperFrame = CGRect(origin: .zero, size: paperSize)
renderer.setValue(paperFrame, forKey: "paperRect")
renderer.setValue(paperFrame, forKey: "printableRect")

HTML文字列をフォーマッターに入力します

let formatter = UIMarkupTextPrintFormatter(markupText: fromHTML)
renderer.addPrintFormatter(formatter, startingAtPageAt: 0)

UIGraphics を使ってHTML文字列をPDFに変換します

UIGraphicsBeginPDFContextToData(pdfData, .zero, [:])
for pageI in 0..<renderer.numberOfPages {
    UIGraphicsBeginPDFPage()
    renderer.drawPage(at: pageI, in: UIGraphicsGetPDFContextBounds())
}
UIGraphicsEndPDFContext()

作成されたPDFデータを一時ファイルに保存します

データをアプリの一時ストレージに保存するのに使える便利な機能を紹介しています

/*
 この関数は、特定の `data` をアプリの一時ストレージに保存します。さらに、そのファイルが存在する場所のパスを返します。
 */
func saveToTempDirectory(data: NSData) -> URL? {
    let tempDirectory = NSURL.fileURL(withPath: NSTemporaryDirectory(), isDirectory: true)
    let filePath = tempDirectory.appendingPathComponent("receipt-" + UUID().uuidString + ".pdf")
    do {
        try data.write(to: filePath)
        return filePath
    } catch {
        print(error.localizedDescription)
        return nil
    }
}

ユーザーへのPDFのプレゼンテーション

さて、デバイスにPDFファイルが保存されているので、 QLPreviewController を通してそれを表示することが可能となりました:

PDFファイルまでのローカルパスを変数に保存する必要があります。

self.PDFpath = savedPath
let previewController = QLPreviewController()
previewController.dataSource = self
present(previewController, animated: true, completion: nil)

さて今度は、デリゲート・ファンクションを実装し、 QLPreviewController がファイルの読み取りとロードする場所を認識できるようにします。

extension ViewController: QLPreviewControllerDataSource {

    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        if self.PDFpath != nil {
            return 1
        } else {
            return 0
        }
    }

    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        guard let pdfFilePath = self.PDFpath else {
            return "" as! QLPreviewItem
        }
        return pdfFilePath as QLPreviewItem
    }

}

:relaxed: Twitter @MszPro

:sunny: 私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7