Edited at

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

More than 1 year has passed since last update.

ディップ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"
]
}