16
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ディップAdvent Calendar 2017

Day 3

【swift】ライブラリを使ってhtmlをpdfデータとして保存しwebViewで表示&動的に書き換える

Last updated at Posted at 2017-12-02

ディップAdventCalendar2017、3日目🌟٩( 'ω' )و


htmlで書いたデータをpdfで保存・作成し複数ページで表示する、
そして中身のデータを動的に書き換える方法です💪

最初はlibHaruという昔からあるpdf作成ライブラリから派生しましたswiftyHaruを使おうとしたのですが、
PDFGeneratorという(自分的に)もっと使いやすいライブラリを見つけました♪( ´▽`)

1.環境

  • Xcode9.1

  • swift3.2

  • podfileに以下を入力

pod 'PDFGenerator'

2.準備

  • .htmlを2つと.css1つを用意
  • .storyboard:viewControllerの上にwebViewを配置
  • webViewをIBOutlet接続
img1.png
  • htmlの中身を動的に差し替えるということで、htmlの中身はこのような形にしております☺️
  • #Data1##Data2#の部分を書き換えるようにします💪
    • なので最初に入れておくhtmlはテンプレデータの役目として使うイメージ
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="pdfSample.css">
  </head>
  <body>
    <p class="color">#Data1##Data2#</p>
  </body>
</html>

3.実装

■html表示用のwebViewを読み込む

pdfを作るにあたり先にhtmlを読み込んでおく必要があるので表示用のwebViewを作成しhtmlを読み込みますo(`ω´ )o

class pdfSampleViewController: UIViewController, UIWebViewDelegate {
    
    // webViewの親view
    @IBOutlet weak var sampleView: UIView!
    // webView
    @IBOutlet weak var webView: UIWebView!
    
    // pdfページ用(html読み込み用)のwebView
    var pdfPage1WebView = UIWebView()
    var pdfPage2WebView = UIWebView()
    // pdfページ用のwebView格納配列
    var webViewArray: [UIWebView] = []

    // MARK: - Life Cycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let frameSize = CGRect(x: 0.0, y: 0, width: sampleView.bounds.width, height: sampleView.bounds.height / 2)
        // viewのサイズの初期化
        pdfPage1WebView = UIWebView(frame: frameSize)
        pdfPage2WebView = UIWebView(frame: frameSize)
        
        pdfPage1WebView.delegate = self
        pdfPage2WebView.delegate = self
    }
}

■htmlのデータを新たに作り保存する

上記で書いたように、テンプレデータに値を入れるため新たにhtmlデータを作成します。
書き換えたい値を別クラス内で辞書として持ちfor文で回すようにしています。
辞書のvalueの方をDBからデータを取ってくるというように変えると良いと思います☺️

// pdfのデータを表示する
func callingPDFData() {
    
    // テンプレートデータのパスを取得する
    if let pdfPage1Path = Bundle.main.path(forResource: "pdfPage1", ofType: "html"),
        let pdfPage2Path = Bundle.main.path(forResource: "pdfPage2", ofType: "html"),
        let cssFormatPath = Bundle.main.path(forResource: "pdfSample", ofType: "css") {
        
        // 編集後のデータを格納する
        var newPdfPage1Data = ""
        var newPdfPage2Data = ""
        var newPdfPage1FilePath = URL(fileURLWithPath: "")
        var newPdfPage2FilePath = URL(fileURLWithPath: "")
        
        do {
            // テンプレートデータの中身を取得する
            let pdfPage1Data = try NSString(contentsOf: URL(fileURLWithPath: pdfPage1Path), encoding: String.Encoding.utf8.rawValue)
            let pdfPage2Data = try NSString(contentsOf: URL(fileURLWithPath: pdfPage2Path), encoding: String.Encoding.utf8.rawValue)
            
            // 取得の中身を置換する
            newPdfPage1Data = pdfPage1Data.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
            newPdfPage2Data = pdfPage2Data.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
            for (key, value) in PdfDataModel().pdfPage1DataDictionary {
                newPdfPage1Data = newPdfPage1Data.replacingOccurrences(of: key, with: value)
            }
            for (key, value) in PdfDataModel().pdfPage2DataDictionary {
                newPdfPage2Data = newPdfPage2Data.replacingOccurrences(of: key, with: value)
            }
            
            // 置換後のデータをドキュメントディレクトリに保存する
            if let dir = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask ).first {
                newPdfPage1FilePath = dir.appendingPathComponent("editPdfPage1.html")
                newPdfPage2FilePath = dir.appendingPathComponent("editPdfPage2.html")
                do {
                    try newPdfPage1Data.write(to: newPdfPage1FilePath, atomically: false, encoding: String.Encoding.utf8)
                    try newPdfPage2Data.write(to: newPdfPage2FilePath, atomically: false, encoding: String.Encoding.utf8)
                } catch {
                }
            }
        } catch {
        }
        // 新しく作ったデータでリクエストを投げる
        let req = NSMutableURLRequest(url: newPdfPage1FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
        let req2 = NSMutableURLRequest(url: newPdfPage2FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
        self.pdfPage1WebView.loadRequest(req as URLRequest)
        self.pdfPage2WebView.loadRequest(req2 as URLRequest)
    }
}
  • データ用クラス
class PdfDataModel {
    
    // 各データ変数を格納
    var pdfPage1DataDictionary: [String : String] = ["#Data1#": "HELLO",
                                                    "#Data2#": " WORLD"
    ]
    // 各データ変数を格納
    var pdfPage2DataDictionary: [String : String] = ["#Data1#": "hello",
                                                    "#Data2#": " world"
    ]
}

■htmlのデータをpdf内に保存する

html用のwebViewが読み込み完了したらpdfで保存する処理を行います💪

  • 読み込みが完了したか判別
// webViewカウント用配列
var webViewCountArray: [UIWebView] = []

// MARK: - UIWebViewDelegate

func webViewDidStartLoad(_ webView: UIWebView) {
        
    webViewCountArray.append(webView)
}
    
func webViewDidFinishLoad(_ webView: UIWebView) {
        
    webViewArray.append(webView)
        
    // htmlファイルが2ページ分とも読み込み完了したらpdfで表示する処理を呼ぶ
    if webViewArray.count == webViewCountArray.count {
        self.getPDFData()
        sampleView.backgroundColor = UIColor.lightGray
    }
}
  • webViewで読み込んだデータをpdfに格納していく
  • ライブラリ側のメソッドにpdfデータとして保存したいviewを継承したオブジェクトを渡すとそれを元にデータを作成してくれます💪💪💪便利!
import PDFGenerator

// pdfファイルの作成、保存
func getPDFData() {
        
    // ドキュメントディレクトリに保存する
    let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last

    if let pdfData = dir?.appendingPathComponent("pdfData.pdf", isDirectory: true) {
        // pdfファイルの作成、保存
        do {
            let data = try PDFGenerator.generated(by: webViewArray)
            try data.write(to: pdfData, options: .atomic)
        } catch {
        }
    }
}

■pdfを呼ぶ

  • pdfを呼び出す処理も同じくライブラリの処理を叩く💪
// 呼び出し
do {
    try PDFGenerator.generate(webViewArray, to: pdfData)
   } catch {
   }
            
   webView.scalesPageToFit = true
   webView.loadRequest(NSMutableURLRequest(url: pdfData) as URLRequest)

3.確認

  • デバックで見てみます。
スクリーンショット 2017-12-03 7.35.59.png

出来た!☺️

コンテナーからDocmentフォルダを見てみます。
スクリーンショット 2018-01-18 10.44.44.png

pdfデータとして保存されているのが確認できました💪✨✨


以下コード全体です↓

import UIKit
import PDFGenerator

class pdfSampleViewController: UIViewController, UIWebViewDelegate {
    
    // webViewの親view
    @IBOutlet weak var sampleView: UIView!
    // webView
    @IBOutlet weak var webView: UIWebView!
    
    // pdfページ用(html読み込み用)のwebView
    var pdfPage1WebView = UIWebView()
    var pdfPage2WebView = UIWebView()
    // webViewカウント用配列
    var webViewCountArray: [UIWebView] = []
    // pdfページ用のwebView格納配列
    var webViewArray: [UIWebView] = []
    
    // MARK: - Life Cycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let frameSize = CGRect(x: 0.0, y: 0, width: sampleView.bounds.width, height: sampleView.bounds.height / 2)
        // viewのサイズの初期化
        pdfPage1WebView = UIWebView(frame: frameSize)
        pdfPage2WebView = UIWebView(frame: frameSize)
        
        pdfPage1WebView.delegate = self
        pdfPage2WebView.delegate = self
        
        self.callingPDFData()
    }
    
    // MARK: - UIWebViewDelegate
    
    func webViewDidStartLoad(_ webView: UIWebView) {
        
        webViewCountArray.append(webView)
    }
    
    func webViewDidFinishLoad(_ webView: UIWebView) {
        
        webViewArray.append(webView)
        
        // htmlファイルが2ページ分とも読み込み完了したらpdfで表示する処理を呼ぶ
        if webViewArray.count == webViewCountArray.count {
            self.getPDFData()
            sampleView.backgroundColor = UIColor.lightGray
        }
    }
    
    // MARK: - Private Method
    
    // pdfファイルの作成、保存
    func getPDFData() {
        
        let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
        if let pdfData = dir?.appendingPathComponent("pdfData.pdf", isDirectory: true) {
            // pdfファイルの作成、保存
            do {
                let data = try PDFGenerator.generated(by: webViewArray)
                try data.write(to: pdfData, options: .atomic)
            } catch {
            }
            // 呼び出し
            do {
                try PDFGenerator.generate(webViewArray, to: pdfData)
            } catch {
            }
            
            webView.scalesPageToFit = true
            webView.loadRequest(NSMutableURLRequest(url: pdfData) as URLRequest)
        }
    }
    
    // pdfのデータを表示する
    func callingPDFData() {
        
        // デフォルトフォーマットのパスを取得する
        if let pdfPage1Path = Bundle.main.path(forResource: "pdfSample1", ofType: "html"),
            let pdfPage2Path = Bundle.main.path(forResource: "pdfSample2", ofType: "html"),
            let cssFormatPath = Bundle.main.path(forResource: "pdfSample", ofType: "css") {
            
            // 編集後のデータを格納する
            var newPdfPage1Data = ""
            var newPdfPage2Data = ""
            var newPdfPage1FilePath = URL(fileURLWithPath: "")
            var newPdfPage2FilePath = URL(fileURLWithPath: "")
            
            do {
                // デフォルトフォーマットの中身を取得する
                let pdfPage1TemplateData = try NSString(contentsOf: URL(fileURLWithPath: pdfPage1Path), encoding: String.Encoding.utf8.rawValue)
                let pdfPage2TemplateData = try NSString(contentsOf: URL(fileURLWithPath: pdfPage2Path), encoding: String.Encoding.utf8.rawValue)
                
                // 取得の中身を置換する
                newPdfPage1Data = pdfPage1TemplateData.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
                newPdfPage2Data = pdfPage2TemplateData.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
                for (key, value) in pdfDataModel().pdfPage1DataDictionary {
                    newPdfPage1Data = newPdfPage1Data.replacingOccurrences(of: key, with: value)
                }
                for (key, value) in pdfDataModel().pdfPage2DataDictionary {
                    newPdfPage2Data = newPdfPage2Data.replacingOccurrences(of: key, with: value)
                }
                
                // 置換後のデータをドキュメントディレクトリに保存する
                if let dir = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask ).first {
                    newPdfPage1FilePath = dir.appendingPathComponent("editPdfPage1.html")
                    newPdfPage2FilePath = dir.appendingPathComponent("editPdfPage2.html")
                    // htmlファイルの作成、保存
                    do {
                        try newPdfPage1Data.write(to: newPdfPage1FilePath, atomically: false, encoding: String.Encoding.utf8)
                        try newPdfPage2Data.write(to: newPdfPage2FilePath, atomically: false, encoding: String.Encoding.utf8)
                    } catch {
                    }
                }
            } catch {
            }
            // 新しく作ったデータでリクエストを投げる
            let req = NSMutableURLRequest(url: newPdfPage1FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
            let req2 = NSMutableURLRequest(url: newPdfPage2FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
            self.pdfPage1WebView.loadRequest(req as URLRequest)
            self.pdfPage2WebView.loadRequest(req2 as URLRequest)
        }
    }
}

class pdfDataModel {
    
    // 各データ変数を格納
    var pdfPage1DataDictionary: [String : String] = ["#Data1#": "HELLO",
                                                    "#Data2#": " WORLD"
    ]
    // 各データ変数を格納
    var pdfPage2DataDictionary: [String : String] = ["#Data1#": "hello",
                                                    "#Data2#": " world"
    ]
}
16
4
3

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
16
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?