iOS11から搭載されたPDFKitが思ったよりすごかった(ハイライト、検索)

## PDFKitのセットアップ

`Main.storyboard`にUIViewを追加して、画面いっぱいに広げます。クラスを`PDFView`に変えておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/122365/21fb5062-f60e-d301-9951-e670a97035dd.png)

`ViewController`に`pdfView`としてつなぎます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/122365/dbe08180-2ebc-52f4-2479-750a174f775c.png)

画像のように`sample.pdf`をルートディレクトリに追加しておきます。

## PDFを表示させてみる

```swift
import UIKit
import PDFKit

class ViewController: UIViewController {

    @IBOutlet weak var pdfView: PDFView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // documentをセットします
        pdfView.document = getDocument()

        // 背景や表示設定を切り替えることができます
        pdfView.backgroundColor = .lightGray
        pdfView.autoScales = true
        pdfView.displayMode = .singlePageContinuous
        pdfView.usePageViewController(true)
    }

    func getDocument() -> PDFDocument? {
        guard let path = Bundle.main.path(forResource: "sample", ofType: "pdf") else {
            print("failed to get path.")
            return nil
        }
        let pdfURL = URL(fileURLWithPath: path)
        let document = PDFDocument(url: pdfURL)
        return document
    }
}
```

いい感じに表示されました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/122365/8709ff70-1ace-e57b-d6ad-7a9b4473beaf.png)


## ハイライトをしてみる

次は以下の画像のように`Highlight`の選択肢をメニューに出して実際にハイライトをできるようにしてみます。デフォルトでは`Copy`/`Select All`の２つしか出てきません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/122365/b51f9db5-fb83-9ea0-4501-29b3cbef7a74.png)

UIMenuControllerを活用します。

```swift
class ViewController: UIViewController {

    @IBOutlet weak var pdfView: PDFView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        pdfView.document = getDocument()

        pdfView.backgroundColor = .lightGray
        pdfView.autoScales = true
        pdfView.displayMode = .singlePageContinuous        
        pdfView.usePageViewController(true)

        createMenu()
    }

    private func getDocument() -> PDFDocument? {
        guard let path = Bundle.main.path(forResource: "sample", ofType: "pdf") else {
            print("failed to get path.")
            return nil
        }
        let pdfURL = URL(fileURLWithPath: path)
        let document = PDFDocument(url: pdfURL)
        return document
    }
    
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(highlight(_:)) {
            return true
        }
        return false
    }

    private func createMenu() {
        let highlightItem = UIMenuItem(title: "Highlight", action: #selector(highlight(_:)))
        UIMenuController.shared.menuItems = [highlightItem]
    }
    
    @objc private func highlight(_ sender: UIMenuController?) {
        guard let currentSelection = pdfView.currentSelection else { return }
        let selections = currentSelection.selectionsByLine()
        guard let page = selections.first?.pages.first else { return }
        
        selections.forEach { selection in
            let highlight = PDFAnnotation(bounds: selection.bounds(for: page), forType: .highlight, withProperties: nil)
            highlight.endLineStyle = .square
            page.addAnnotation(highlight)
        }
        
        pdfView.clearSelection()
    }
}
```


## 検索結果をハイライトしてみる

```swift

class ViewController: UIViewController {
    ...

    func find(text: String) {
        let selections = pdfView.document?.findString(text, withOptions: .caseInsensitive)
        guard let page = selections?.first?.pages.first else { return }
        selections?.forEach { selection in
            let highlight = PDFAnnotation(bounds: selection.bounds(for: page), forType: .highlight, withProperties: nil)
            highlight.endLineStyle = .square
            page.addAnnotation(highlight)
        }
    }
}
```

として、`find("hogehoge")`を呼ぶとハイライトがかかります。


## ハイライトのタップを検知してみる

`NotificationCenter`を活用します。

```swift

    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        
        ...

        NotificationCenter.default.addObserver(forName: .PDFViewAnnotationHit, object: nil, queue: nil, using: notified)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        NotificationCenter.default.removeObserver(self)
    }

    @objc private func notified(notification: Notification) {
        print("Highlightがタップされました！")
    }

    ...
```


サンプルコードはこちら
https://github.com/ngo275/PDFKitSample

## 参考

https://developer.apple.com/documentation/pdfkit/pdfannotation
https://developer.apple.com/documentation/pdfkit/pdfdocument
https://developer.apple.com/documentation/uikit/uimenucontroller