LoginSignup
11
4

More than 3 years have passed since last update.

[iOS]カスタムドキュメントにサムネイルを表示する

Last updated at Posted at 2020-03-09

iOSとファイルのサムネイル

iOSでは、Mac同様にファイルを扱う機構が存在します。
標準では「ファイル」アプリを通してiCloud Driveやローカルのファイルにアクセス出来ますし、それぞれのアプリではUIDocumentPickerViewControllerを使ってアクセスすることも出来ます。

IMG_1408.jpg

これらのファイルブラウザではjpegやpngなど標準的なファイルフォーマットのサムネイル表示が標準で実装されています。

image.png

また、サムネイルの表示に対応していないファイルもQuickLookThumbnailing.frameworkを使った拡張機能をアプリにバンドルしてインストールすることでサムネイル表示に対応させることが出来ます。
このエントリでは、iOSにおけるサムネイル拡張機能の実装と予備知識を紹介します。

実装の方法

「Thumbnail Extension」は、アプリの拡張機能に当たるため単体でインストールさせることが出来ません。
メインのアプリにバンドルする形で配布する必要があります。
ここでは、適当にアプリプロジェクトを作成し「Thumbnail Extension」用のターゲットを追加します。

スクリーンショット 2020-03-09 20.48.53.png

ターゲットを追加すると、ファイルツリーに次のような構造が作成されます。

スクリーンショット 2020-03-09 20.53.06.png

ThumbnailProvider.swiftにサムネイル生成の処理を記述し
Info.plistにはどのファイルに対して処理を行うかを記述します。

Info.plistの設定

Info.plistを開くと次のような構造になっています。

スクリーンショット 2020-03-09 20.58.36.png

QLSupportedContentTypesにはカスタムドキュメントのUTIを、QLThumbnailMinimumDimensionにはサムネイルの最小サイズを記述します。

QLSupportedContentTypesに設定するUTIの調べ方

UTIは次のコマンドで確認することができます。

$ mdls file.ext

今回はSwiftファイルを例に実行した結果を見てみましょう。

output
kMDItemContentType                     = "public.swift-source"
kMDItemContentTypeTree                 = (
    "public.swift-source",
    "public.source-code",
    "public.plain-text",
    "public.text",
    "public.data",
    "public.item",
    "public.content"
)

QLSupportedContentTypesにはkMDItemContentTypeTreeに表示されているタイプを全て記入します。
kMDItemContentTypeだけだと上手く処理が呼ばれないことが多いです。

QLThumbnailMinimumDimensionの値

基本的には弄る必要は無いかと思います。デフォルトは17です。

QLThumbnailProviderの実装

ThumbnailProvider.swiftの中は次のような構造になっています。

import UIKit
import QuickLookThumbnailing

class ThumbnailProvider: QLThumbnailProvider {
    override func provideThumbnail(for request: QLFileThumbnailRequest, _ handler: @escaping (QLThumbnailReply?, Error?) -> Void) {
        handler(QLThumbnailReply(contextSize: request.maximumSize, currentContextDrawing: { () -> Bool in
            return true
        }), nil)
    }
}

構造としては非常にシンプルで、provideThumbnailを実装するだけです。

QLFileThumbnailRequestからは入力されたファイルパスなどが取れます。
この入力を元に画像を描画する処理をQLThumbnailReplyに詰めて返します。

QLThumbnailReply

画像のURLを返すinit(imageFileURL:)
CGContextを受け取るinit(contextSize:, drawing:)
描画コンテキストが開始されているinit(contentSize: currentContextDrawing:)
があります。

スクリーンショット 2020-03-09 21.18.01.png

init(imageFileURL:)はファイルの構造の中にサムネイル用のファイルが配置されているときなどに適しています。

また、drawing系はサムネイルの取得時にCGImageを受け取る場合や、文字や画像を合成したいときに利用するのに適しています。

let thumbnail = ThumbnailLoader(url: url).load()
let reply = QLThumbnailReply(contextSize: request.maximumSize, currentContextDrawing: {
  let frame = CGRect(origin: .zero, size: request.maximumSize)
  thumbnail?.draw(in: frame)
  return true
})
handler(reply, nil)

ファイルの読み込み

QLFileThumbnailRequestに入っているfileURLをそのまま使ってアクセスすると、アクセスがブロックされることがあります。
fileURLは必ずUIDocumentを介してアクセスする必要があるので、注意する必要があります。
次のように、UIDocumentを継承したクラスに、openメソッドを実装します。
super.openを呼ばない点に注意しましょう。
また、UIDocumentは必ずサブクラスを作る必要があります

class CustomDocument: UIDocument {
    override func open(completionHandler: ((Bool) -> Void)? = nil) {
        completionHandler?(true)
    }
}

カスタムクラスを作ったら、次のようにopenのクロージャの中でファイルにアクセスします。

class ThumbnailProvider: QLThumbnailProvider {
    override func provideThumbnail(for request: QLFileThumbnailRequest, _ handler: @escaping (QLThumbnailReply?, Error?) -> Void) {
        CustomDocument(fileURL: request.fileURL).open { (_) in
            let thumbnail = ThumbnailLoader(url: url).load()
            let reply = QLThumbnailReply(contextSize: request.maximumSize, currentContextDrawing: {
              let frame = CGRect(origin: .zero, size: request.maximumSize)
              thumbnail?.draw(in: frame)
              return true
            })
            handler(reply, nil)
        }
    }
}

QLThumbnailProviderの制限

QLThumbnailProviderは、メモリの使用量を50MB以内に抑える必要があります
50MBをオーバーすると拡張機能のプロセスは強制終了させられます。
余分なメモリを確保しないよう、Streamやポインタを上手に使ってサムネイルを生成する処理を書きましょう。

デバッグ

ターゲットをThumbnail Extensionに設定し、ビルドするとデバッグすることができます。
もちろんbreak pointを仕込むことも出来ます。
provideThumbnailが呼ばれないようであれば、utiの設定を見直してみましょう。

スクリーンショット 2020-03-09 21.41.11.png

参考

利用例

vearではvrmファイルのサムネイルをThumbnail extensionで実装しています。

11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4