この記事では、ノートや紙、本などの書類をスキャンする実装方法について解説していきたいと思います。
今回は、VisionKitというライブラリを使って実装していきたいと思います。
書類スキャンとは?
要は、書類を斜めに撮っても真っ直ぐな画像に直してくれる&文字がはっきりとうつしてくれるというのが、書類スキャンです。
実装方法
今回はSwiftUIで実装していきたいと思います。UI実装、機能実装、機能反映の3ステップでやっていきたいと思います。
UI実装
ContentViewの中身を以下のコードに書き換えてください。
var body: some View {
    NavigationView {
        List {
        }
        .navigationTitle("Vinsonkitデモ")
        .toolbar {
            ToolbarItem {
                Button("スキャンする") {
                }
            }
        }
    }
}

これでナビゲーションバーとリストとナビゲーションバーボタンがある簡単なUIができました。(今の所リストの中身はなし。)
機能実装
まず、新規ファイルを作成しましょう。

フォルダをクリックしてcommand + nを押してください。

①Swiftを選択
②Nextをクリック

①Save As:に、ScannerModel.swiftと入力
②Createをクリック
以下のコードを、追加してインポートする
import VisionKit

以下のコードを追記する
final class ScannerModel: NSObject, ObservableObject {
    @Published var imageArray: [UIImage] = []
    
    func getDocumentCameraViewController() -> VNDocumentCameraViewController {
        let vc = VNDocumentCameraViewController()
        vc.delegate = self
        return vc
    }
}
extension ScannerModel: VNDocumentCameraViewControllerDelegate {
    func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
        controller.dismiss(animated: true)
    }
    
    func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
        for i in 0..<scan.pageCount {
            self.imageArray.append(scan.imageOfPage(at: i))
        }
        controller.dismiss(animated: true)
    }
}

メソッドごとに何をおこなっているか簡単に解説すると、
getDocumentCameraViewController()は、カメラを起動したときに呼ばれるメソッドで、ViewControllerをdelegateして返しています。
documentCameraViewControllerDidCancel()は、キャンセルボタンを押したときに呼ばれるメソッドで、画面を閉じる処理をしています。
documentCameraViewController()は、カメラを撮った後に呼ばれるメソッドで、撮った画像を配列に入れて画面を閉じています。
これだけで機能の作成は完了です。
機能反映
では、上記で作成した機能を、ボタンやリストに反映していきましょう。
まずは、modelをインスタンス化します。以下のコードをContentView内で宣言してください。
@ObservedObject var scannerModel = ScannerModel()

ボタンを押したらカメラを起動するようにしましょう。以下のコードをButtonの中に貼り付けてください。
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
let window = windowScene?.windows
window?.filter({$0.isKeyWindow}).first?.rootViewController?.present(scannerModel.getDocumentCameraViewController(), animated: true)

次に、取った写真をリストに表示するようにしましょう。以下のコードをListの中に貼り付けてください。
ForEach(scannerModel.imageArray, id: \.self) { image in
    Image(uiImage: image)
        .resizable()
        .aspectRatio(contentMode: .fit)
}

カメラを使うためには、許可アラートを表示させないといけません。Info.plistに追加しましょう。

①プロジェクトを選択
②TARGETSを選択
③Infoを選択
④プラスボタンを押して新しく追加し、KeyにPrivacy - Camera Usage Description、Valueに書類をスキャンするためにカメラを使用します。と入力
これで、このアプリは完成です!
import SwiftUI
struct ContentView: View {
    @ObservedObject var scannerModel = ScannerModel()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(scannerModel.imageArray, id: \.self) { image in
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                }
            }
            .navigationTitle("Vinsonkitデモ")
            .toolbar {
                ToolbarItem {
                    Button("スキャンする") {
                        let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
                        let window = windowScene?.windows
                        window?.filter({$0.isKeyWindow}).first?.rootViewController?.present(scannerModel.getDocumentCameraViewController(), animated: true)
                    }
                }
            }
        }
    }
}
import Foundation
import VisionKit
final class ScannerModel: NSObject, ObservableObject {
    @Published var imageArray: [UIImage] = []
    
    func getDocumentCameraViewController() -> VNDocumentCameraViewController {
        let vc = VNDocumentCameraViewController()
        vc.delegate = self
        return vc
    }
}
extension ScannerModel: VNDocumentCameraViewControllerDelegate {
    func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
        controller.dismiss(animated: true)
    }
    
    func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
        for i in 0..<scan.pageCount {
            self.imageArray.append(scan.imageOfPage(at: i))
        }
        controller.dismiss(animated: true)
    }
}
実行してみる
では、実行してみましょう。シミュレーターだとカメラ機能が使えないので、MacとiPhoneを繋げてiPhoneで確認してください。
上記の動画のように、カメラでスキャンしてSAVEボタンを押すと、リストに画像が反映されていると思います。
最後に
一応、GitHubに載せておきます〜
ちなみに、この機能は、BookMasterで使おうかなと思っています〜


