ディップ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接続
- 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.確認
- デバックで見てみます。
出来た!☺️
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"
]
}