色々あって個人の漫画アプリを作る案件を請け負わせて頂きました。
その実装の際に、サーバーからPDFをDL、保存したり、WebViewでなく横スワイプでページをめくれるようにしたり…みたいなことをしようとして、とても(2週間ほど)躓いたので、知見を共有しようかと思います。
環境
Mac OS X Yosemite 10.10.4
XCode 6.4
CocoaPods 0.38.2
M13PDFKit 1.0.2
プロジェクトの立ち上げ
XCodeを起動してCreate a new Xcode projectを選び、iOS Application -> Single View Application を選択。 名前を適当に設定してLanguageはSwiftに。出来たら保存!
PDFをDL、保存する仕組みを作る
とりあえずこれが出来ないと表示もへったくれもないので先に実装しちゃいましょう。
トップ画面のViewControllerを別に作って編集していきます。メニューバーからFile -> New -> Fileを選択してください。
次にCocoa Touch Classを選択して
Sub Class をUIViewControllerに設定。名前は 雑にTopViewControllerとかでいいでしょう。
出来たらプロジェクトのルートフォルダ内にある、プロジェクト名と同じフォルダの中に保存してください。AppDelegate.swiftとかが入ってるところです。
続いてStoryBoardを開いてViewControllerの割り当て、ボタンの配置等を進めていきましょう。
UIButtonを2つ、ProgressBar1つをこんな感じに配置してください。
画像ではMainViewControllerとか書かれてますが、気にしないでください。
上の〜〜ViewControllerと書かれているところをクリックするとViewControllerをユーティリティエリア(右側のやつ)から弄れるようになります。
ユーテリティエリアで下の画像で選択されている、左から3つ目のタブをクリックしClassからTopViewControllerを選択してあげてください。
その後、上のバーの右の方にある、丸が2つ重なったようなボタンをクリックして下さい。コードが右に表示されるはずです。
その状態でCtrlを押したままボタンをクリックしてそのままコードにドラッグしてください。ボタンが変数として登録されます。
以下のように接続してください。
Downloadと書かれたボタン
Connection: Outlet
Name: dl_button
Downloadと書かれたボタン
Connection: Action
Name: tapDlButton
ProgressBar
Connection: Outlet
Name: progress_bar
出来たら以下のようにコードを書いてください。
import UIKit class TopViewController: UIViewController, NSURLSessionDownloadDelegate { @IBOutlet weak var dl_button: UIButton! @IBOutlet weak var progress_bar: UIProgressView! let path = NSSearchPathForDirectoriesInDomains( .DocumentDirectory, .UserDomainMask, true) override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. //self.show_button.hidden = true; progress_bar.progress = 0 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } /* // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { // Get the new view controller using segue.destinationViewController. // Pass the selected object to the new view controller. } */ @IBAction func tapDlButton(sender: AnyObject) { self.downloadWithFile() } func downloadWithFile() { let url = NSURL(string: "http://cdn.oreillystatic.com/oreilly/booksamplers/9781491908693_sampler.pdf") let config = NSURLSessionConfiguration.defaultSessionConfiguration() let session = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue()) let task = session.downloadTaskWithURL(url!) task.resume() } func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { progress_bar.progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite) } func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) { progress_bar.progress = 1.0 let data = NSData(contentsOfURL: location)! if data.length == 0 { NSLog("Error") } else { NSLog("Download Success") self.show_button.hidden = false let _path = path[0].stringByAppendingPathComponent("test.pdf") let success = data.writeToFile(_path, atomically: true) if success { println("Save Success") } } } func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { if error != nil{ session.invalidateAndCancel() }else{ session.finishTasksAndInvalidate() } NSLog("OK") } }
ダウンロードにはNSURLSessionというクラスを用います。classの行にNSURLSessionDelegate
をつけるのを忘れないで下さい。
progress_bar.progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
によってダウンロードの進み具合を可視化しています。
保存にあたっては、Document配下に保存するためまずNSSearchPathForDirectoriesInDomains
でDocumentのパスを取得し、let _path = path[0].stringByAppendingPathComponent("test.pdf")
によって細かく保存先を指定しています。
その後、data.writeToFile(_path, atomically: true)
でファイルを保存しました。
これで保存までは完了しましたね。次は表示しましょう。
CocoaPodsでライブラリをダウンロードしよう
次はPDFをよしなに表示してくれるライブラリを導入しましょう。今回用いるライブラリはこちらです。
では早速導入しましょう。CocoaPodsの導入方法は省きます。
ターミナルからプロジェクトのルートフォルダにアクセスし、
$ pod init
を実行してください。
実行して出てきたPodfileは以下のように記述してください。
# Uncomment this line to define a global platform for your project # platform :ios, '6.0' source 'https://github.com/CocoaPods/Specs.git' use_frameworks! pod 'M13PDFKit', '1.0.2'
書けたら
$ pod install
を実行して下さい。
無事に成功したのを確認したら、今開いてるXCodeのプロジェクトを閉じて、新しく生成されたプロジェクト名.xcworkspace
を開いてください。
これでライブラリのダウンロードは完了です。
ライブラリを用いてPDFを表示しよう。
ではこのライブラリを用いて、PDFを表示します。
まずは表示するためのViewControllerを準備しましょう。StoryBoardを開いてください。
新しくViewControllerを設置し、TopViewControllerのShow PDFと書かれたボタンをCtrlを押したままクリックし、新しいViewControllerへドラッグしてください。
すると黒いメニューが表示されるので、その中にあるshowを押してあげて下さい。
次に、TopViewControllerを設定した要領で、この新しいViewControllerにもクラスを割り当ててあげましょう。割り当てるクラスはPDFKBasicPDFViewer
です。
ここまで出来たら、またTopViewControllerを編集していきます。
まず一番上のインポートを以下のように変更してください。
import UIKit import M13PDFKit
これでM13PDFKitライブラリがこのファイル内で扱えるようになりました。
続いて一番下に以下のコードを書き足してください。
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { var viewer: PDFKBasicPDFViewer = segue.destinationViewController as! PDFKBasicPDFViewer let _path = path[0].stringByAppendingPathComponent("test.pdf") var document = PDFKDocument(contentsOfFile: _path, password: nil) viewer.loadDocument(document) }
_path
に先ほど指定したPDFの保存先を格納し、 var document = PDFKDocument(contentsOfFile: _path, password: nil)
でPDFKDocumet
に変換しています。
これでPDFViewerは完成です。実行して確認してみましょう。
おわりに
Swift(というかiOSアプリ開発)分からないことが多すぎます…。Objective-Cのライブラリと連携させる所とかとても詰まりました。
最終的にはSwiftに明るい方に助けて頂いて何とかなりました。本当にありがとうございます。個人の漫画アプリを作ろうとしている方々、是非この知見を活かしてください。
漫画アプリ案件、何とかなってくれという気持ちで一杯だ…頑張っていこう。