この記事は2019年01月04日に投稿しました。
目次

- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2017/12/26
- メディア: 単行本
- この商品を含むブログ (1件) を見る
1. はじめに
こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回はPWEditorで使用しているSwiftでPDFファイルを表示する方法についてです。
2. SwiftでPDFファイルを表示する
SwiftでPDFファイルを表示する方法ですが、iOS11以降で利用可能なPDFKitを利用した方法ではない方法を紹介します。
PWEditorで実装したコードをになりますが、以下の機能があります。
- PDFファイルを表示します。
- 左右のスワイプにより、ページ送り・戻りが可能です。
- ピンチイン/ピンチアウトで縮小・拡大(0.1倍〜10倍)表示可能です。
- ダブルタップによる拡大表示が可能です。
- シングルタップで等倍表示に戻ります。
- 画面の向きにより適切にPDFを表示します。
実装例
import UIKit import CoreGraphics /** PDFビュークラス */ class PDFView: UIView { // ページ var page: CGPDFPage? /** PDFを描画します。 - Parameter rect: 描画する領域 */ override func draw(_ rect: CGRect) { guard let page = page else { return } guard let context = UIGraphicsGetCurrentContext() else { return } let rectWidth = rect.size.width let rectHeight = rect.size.height context.translateBy(x: 0, y: rectHeight) context.scaleBy(x: 1.0, y: -1.0) let box = page.getBoxRect(.artBox) let boxWidth = box.size.width let boxHeight = box.size.height let xScale: CGFloat = rectWidth / boxWidth let yScale: CGFloat = rectHeight / boxHeight let scale = min(xScale, yScale) let tx: CGFloat = (rectWidth - boxWidth * scale) / 2.0 let ty: CGFloat = (rectHeight - boxHeight * scale) / 2.0 if UIDevice.current.orientation.isPortrait { // 縦向きの場合 context.translateBy(x: 0, y: ty) } else { // 横向きの場合 context.translateBy(x: tx, y: 0) } context.scaleBy(x: scale, y: scale) context.drawPDFPage(page) } }
import UIKit /** PDF表示クラス */ class PdfViewController: UIScrollViewDelegate { /// スクロールビュー @IBOutlet weak var scrollView: UIScrollView! // PDFビュー @IBOutlet weak var pdfView: PDFView! /// パス名 var pathName: String /// ファイル名 var fileName: String /// ページ数 var pages = 1 /// 現在のページ番号 var nowPage = 1 /// PDFドキュメント var doc: CGPDFDocument! // MARK: - Initializer /** イニシャライザ */ required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } /** イニシャライザ - Parameter pathName: パス名 - Parameter fileName: ファイル名 */ init(pathName: String, fileName: String) { // 引数のデータを保存します。 self.pathName = pathName self.fileName = fileName // スーパークラスのメソッドを呼び出します。 super.init(nibName: nil, bundle: nil) } // MARK: - UIViewController /** インスタンスが生成された時に呼び出されます。 */ override func viewDidLoad() { // スーパークラスのメソッドを呼び出します。 super.viewDidLoad() // 画面名にファイル名を設定します。 navigationItem.title = fileName // デリゲートを設定します。 scrollView.delegate = self // デフォルトの拡大率を設定します。 scrollView.zoomScale = 1 // ジェスチャーを追加します。 addGesture() // 通知設定を追加します。 addNotification() // ファイルデータを取得します。 getFileData(action: setPdfDoc(_:)) } /** ジェスチャーを追加します。 */ func addGesture() { // ダブルタップジェスチャー let doubleTapAction = #selector(doubleTap(_:)) let doubleTapGesture = UITapGestureRecognizer(target: self, action: doubleTapAction) doubleTapGesture.numberOfTapsRequired = 2 pdfView.addGestureRecognizer(doubleTapGesture) // シングルタップジェスチャー let singleTapAction = #selector(singleTap(_:)) let singleTapGesture = UITapGestureRecognizer(target: self, action: singleTapAction) singleTapGesture.numberOfTapsRequired = 1 singleTapGesture.require(toFail: doubleTapGesture) pdfView.addGestureRecognizer(singleTapGesture) // 左スワイプジェスチャー let leftSwipeAction = #selector(leftSwipe(_:)) let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: leftSwipeAction) leftSwipeGesture.direction = .left pdfView.addGestureRecognizer(leftSwipeGesture) // 右スワイプジェスチャー let rightSwipeAction = #selector(rightSwipe(_:)) let rightSwipeGesture = UISwipeGestureRecognizer(target: self, action: rightSwipeAction) rightSwipeGesture.direction = .right pdfView.addGestureRecognizer(rightSwipeGesture) } /** 通知機能を追加します。 */ func addNotification() { let action = #selector(orientationDidChange(_:)) let center = NotificationCenter.default let name = UIDevice.orientationDidChangeNotification center.addObserver(self, selector: action, name: name, object: nil) } /** レイアウトされた後に呼び出されます。 */ override func viewDidLayoutSubviews() { // スーパクラスのメソッドを呼び出します。 super.viewDidLayoutSubviews() // PDFViewのサイズをScrollViewのサイズに合わせます。 let width = scrollView.frame.width let height = scrollView.frame.height let pdfSize = CGSize(width: width, height: height) pdfView.frame.size = pdfSize scrollView.contentSize = pdfView.frame.size // PDFViewのinsetもScrollViewのサイズに合わせます。 let pdfInset = UIEdgeInsets(top: 0, left: 0, bottom: height, right: width) pdfView.frame.inset(by: pdfInset) // contentInsetを更新します。 updateScrollInset() } /** 画面が閉じられた後に呼び出されます。 - Parameter animated: アニメーション指定 */ override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) // 通知設定を削除します。 removeNotification() } /** 通知機能を削除します。 */ func removeNotification() { let center = NotificationCenter.default center.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil) } /** 画面が回転した時に呼び出されます。 */ @objc func orientationDidChange(_ notification: NSNotification) { // PDFビューを更新します。 for subView in scrollView.subviews { subView.setNeedsDisplay() } } /** スクロールビューでズームした時に呼び出されます。 - Parameter scrollView: スクロールビュー - Returns: ズームされるビュー */ func viewForZooming(in scrollView: UIScrollView) -> UIView? { return pdfView } /** スクロールビューのズーム倍率が変更された時に呼び出されます。 - Parameter scrollView: スクロールビュー */ func scrollViewDidZoom(_ scrollView: UIScrollView) { // ズームのタイミングでcontentInsetを更新します。 updateScrollInset() } /** ScrollViewのcontentInsetを更新します。 */ func updateScrollInset() { // PDFViewの大きさからcontentInsetを再計算します。 // 0を下回らないようにします。 let top = max((scrollView.frame.height - pdfView.frame.height) / 2, 0.0) let left = max((scrollView.frame.width - pdfView.frame.width) / 2, 0.0) scrollView.contentInset = UIEdgeInsets(top: top, left: left, bottom: 0.0, right: 0.0); } /** 左スワイプした時に呼び出されます。 - Parameter sender: ジェスチャーオブジェクト */ @objc func leftSwipe(_ sender: UISwipeGestureRecognizer) { if nowPage == pages { // 最終ページの場合、何もせず終了します。 return } // 現在のページ数を更新し、ページを変更します。 nowPage += 1 changePage() } /** 右スワイプした時に呼び出されます。 - Parameter sender: ジェスチャーオブジェクト */ @objc func rightSwipe(_ sender: UISwipeGestureRecognizer) { if nowPage == 1 { // 先頭ページの場合、何もせず終了します。 return } // 現在のページ数を更新し、ページを更新します。 nowPage -= 1 changePage() } /** ページを変更します。 */ func changePage() { let page = doc.page(at: nowPage) pdfView.page = page for subView in scrollView.subviews { subView.setNeedsDisplay() } } /** シングルタップした時の処理を行います。 - Parameter sender: タップジェスチャーオブジェクト */ @objc func singleTap(_ sender: UITapGestureRecognizer) { // ズーム倍率を1倍に設定します。 scrollView.setZoomScale(1.0, animated: true) } /** ダブルタップした時の処理を行います。 - Parameter sender: タップジェスチャーオブジェクト */ @objc func doubleTap(_ sender: UITapGestureRecognizer) { if (scrollView.zoomScale < scrollView.maximumZoomScale) { // 現在のズーム倍率が最大ズーム倍率より小さい場合 let scale = scrollView.zoomScale * 2 let center = sender.location(in: sender.view) let rect = zoomRectForScale(scale: scale, center: center) scrollView.zoom(to: rect, animated: true) } else { // 現在のズーム倍率が最大ズーム倍率の場合 // ズーム倍率を1倍に設定します。 scrollView.setZoomScale(1.0, animated: true) } } /** スケールに合わせたズームされた領域を設定します。 - Parameter scale: スケール - Parameter center: 中央位置 - Returns: ズームされた領域 */ func zoomRectForScale(scale: CGFloat, center: CGPoint) -> CGRect{ let frameSize = scrollView.frame.size let width = frameSize.width / scale let height = frameSize.height / scale let size = CGSize(width: width, height: height) let x = center.x - size.width / 2.0 let y = center.y - size.height / 2.0 let origin = CGPoint(x: x, y: y) let rect = CGRect(origin: origin, size: size) return rect } /** PDFドキュメントを設定します。 - Parameter data: PDFデータ */ @objc func setPdfDoc(_ data: Data) { // PDFドキュメントを生成します。 guard let provider = CGDataProvider(data: data as CFData) else { return } guard let doc = CGPDFDocument(provider) else { return } self.doc = doc // 総ページ数を取得します。 pages = doc.numberOfPages // 現在のページを設定し、表示します。 let page = doc.page(at: nowPage) pdfView.page = page pdfView.backgroundColor = UIColor.white for subView in scrollView.subviews { subView.setNeedsDisplay() } } // MARK: - Override /** ファイルデータを取得します。 サブクラスで実装します。 ※PWEditorでこのクラスは、各ストレージで共通にしています。 そのため各ストレージごとに異なるファイルデータの取得処理は 各ストレージごとのサブクラスで実装するようにしています。 - Parameter action: ファイルデータ取得後に呼び出すクロージャー */ func getFileData(action: @escaping (_ data: Data) -> Void) { // デフォルトはエラーにします。 abort() }
3. おわりに
実装には以下のサイトを参考にさせていただきました。
ありがとうございます。
【iOS開発】Swiftで簡易PDFビューアーを作成(PDFを読み込み、表示)
画像をダブルタップとピンチイン・ピンチアウトで拡大・縮小する Swift3編
お仕事決まれば全額キャッシュバック!転職特化型Ruby実践研修【ポテパンキャンプ】

詳細! Swift iPhoneアプリ開発入門ノート iOS12 + Xcode 10対応
- 作者: 大重美幸
- 出版社/メーカー: ソーテック社
- 発売日: 2018/11/03
- メディア: 単行本
- この商品を含むブログを見る
紹介している一部の記事のコードはGitlabで公開しています。
興味のある方は覗いてみてください。
私が勤務しているニューラルでは、主に組み込み系ソフトの開発を行っております。
弊社製品のハイブリッドOS Bi-OSは高い技術力を評価されており、特に制御系や通信系を得意としています。
私自身はiOSモバイルアプリやウィンドウズアプリを得意としております。
ソフトウェア開発に関して相談などございましたら、お気軽にご連絡ください。
また一緒に働きたい技術者の方も随時募集中です。
興味がありましたらご連絡ください。
EMAIL : info-nr@newral.co.jp / m-futamata@newral.co.jp
TEL : 042-523-3663
FAX : 042-540-1688