📱Zoi for iPhone
NEW GAME! のコマ検索 - みずぴー日記の成果を用いて、iPhoneからNEW GAME!のコマ検索を行なえるようにした。
経緯
ニコニコでやっているアニメの一挙放送を見ると、PCの前に4〜6時間ほど拘束される。 そのときに、なんとなくXcodeを立ちあげてコードを書きはじめてしまった。
桜Trick、一挙放送みながらiOSアプリつくってたら、だいぶ便利な感じがでてきた。 pic.twitter.com/UIzmPc98DF
— mzp (@mzp) February 21, 2016
機能
コマのインクリメンタル検索
NEW GAME! のコマ検索 - みずぴー日記に各コマのセリフを入力済みなので、それを移植してインクリメンタルに検索できるようにした。
またUIActivityViewControllerを用いたアプリ間連携も行なえる。
Spotlight対応
App Searchに対応したため、Spotlightから検索ができる。
Spotlightのインデックスに登録するタイミングは、悩んだが、
- 初回起動時
- 明示的に再インデックスを指示した時
の2つにした。 最初は、初回起動時のときのみにしていたが、インデックス処理でエンバグしたときの対応が面倒だったので、再インデックスボタンを追加した。
インデックスへの追加はApp Search プログラミングガイド: 検索の基本にあるコードをほぼそのまま使っている。
func add(items : [ZoiJson.Item], complete: () -> ()) { let si = items.map { self.searchableItemFor($0) } CSSearchableIndex.defaultSearchableIndex().indexSearchableItems(si) { error in if error != nil { print(error?.localizedDescription) } else { complete() } } private func searchableItemFor(item : ZoiJson.Item) -> CSSearchableItem { let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypePNG as String) attributeSet.title = item.script if let image = ZoiImage.image(item) { attributeSet.thumbnailData = UIImagePNGRepresentation(image) } return CSSearchableItem(uniqueIdentifier: item.path, domainIdentifier: "zoi", attributeSet: attributeSet) }
newgame:// 対応
ぐらばくさんが提案していた、newgame:// をカスタムURLスキームとして取り込んだ。(参考: ぐらばく on Twitter: "newgame://3/65/2/2 もいい https://t.co/hC6t3KxYj0")
https://twitter.com/mzp/status/702863419226005505
newgame://巻数/ページ番号/列/行
というフォーマットだが、目次等の存在により書籍のページ番号と先頭からページ数は一致しないので、そこは補正した。 また、ページ内の何コマ目なのかは保持しているが、何列目かは保持していないので、そこの補正も行なっている。
func toPath(url : NSURL) -> String? { if let vol = url.host, let paths = url.pathComponents, let page = Int(paths[1]), let col = Int(paths[2]), let row = Int(paths[3]) { let pageStr = NSString(format: "%03d", page + 2) let pos = (col - 1) * 3 + row return "data/vol\(vol)/vol\(vol)_\(pageStr)_\(pos).jpeg" } else { return nil } }
OnDemandResource対応
Testflightにアップロードしようとしたが100MBを越えてしまった。そこで、OnDemandResourceを利用し、必要になったタイミングで画像を取得するようにした。
OnDemandResourceの使い方はオンデマンドリソースでiOSアプリを軽くする - Qiitaを参考にした。
またOnDemandResourceで取得した画像を、ImageViewに非同期で設定するために https://github.com/Haneke/HanekeSwiftを用いてる。標準ではOnDemandResourceからの取得はできなかったので、独自のFetcherを作った。
import Foundation import Haneke class OnDemandResource { func fetch(fail : NSError? -> Void, succeed : Void -> Void) { let request = NSBundleResourceRequest(tags: Set(arrayLiteral: "zoi")) request.conditionallyBeginAccessingResourcesWithCompletionHandler() { resourceAvailable in if resourceAvailable { succeed() } else { request.beginAccessingResourcesWithCompletionHandler() { error in if error == nil { succeed() } else { fail(error) } } } } } } class OnDemandFetcher<T : DataConvertible> : Fetcher<T> { private let resource = OnDemandResource() private let getValue : () -> T.Result? init(key: String, @autoclosure(escaping) value getValue : () -> T.Result?) { self.getValue = getValue super.init(key: key) } override func fetch(failure fail: ((NSError?) -> ()), success succeed: (T.Result) -> ()) { resource.fetch(fail) { if let result = self.getValue() { self.main { succeed(result) } } else { self.main { fail(nil) } } } } override func cancelFetch() {} private func main(f : () -> ()) { dispatch_async(dispatch_get_main_queue(), f) } } // 使い方 let fetcher = OnDemandFetcher<UIImage>(key: self.item.path, value: UIImage(named: "zoi.png")) self.image.hnk_setImageFromFetcher(fetcher, placeholder: UIImage(named: "placeholder.jpeg"), format: Format<UIImage>(name: "original"))