アプリ作ってて、ある view をタッチしたら・・・というのを検出する話。
いつもは、対象となる uiview の sub class を作って、delegate を通して、あーめんどくセー・・・という流れだったのですが、今日、こんな書き方を知った。
アプリ作ってて、ある view をタッチしたら・・・というのを検出する話。
いつもは、対象となる uiview の sub class を作って、delegate を通して、あーめんどくセー・・・という流れだったのですが、今日、こんな書き方を知った。
CloudKit を使おうと思い、Capability > iCloud > CloudKit を On にする。すると、steps にエラーの表示が。こんな感じ。
! Add the iCloud feture to your App ID.
! Add iCloud Containers to your App ID.
ってことで、provisioning profile とかをちゃんと設定してからでないと CloudKit を使えないのね。念のため、CloudKit dashboard のボタンを押してみたけれども、表示されなかった。
provisioning profile の設定って鬼門。すんなりいったことがかなり少ないように思います。ま、でも、実機に WiFi 経由でアプリを送ったりアップデートできるようになったりするので、実は便利なことが多くいいんですが。
明日やろう。
CoreML を使って文字の認識をしたくて、色々調べているのですが、Swift の記事ばかりで、Objective-C の例が全然ない。Swift の記述を Objective-C に置き換えればよい話なのですが、Swift を理解していないので辛い・・・。書籍は持っているのですが、なかなか覚えられないものです。そんな中、ようやく Objective-C で記述できたので、参考までに記載。
まず、カメラを使って写真を撮ります。その写真を VGG16 model で画像認識させて、写っている物体を認識させます。いろいろな記事を読んでいると、スッゲー簡単みたいなことが書いてありますが、Objective-C の記載ではないので前準備の変数定義部分からして???なんですが、ようやく形になったと。具体的にはこんな感じ・・・
意外とハマるのが、結果の取り出し部分で、配列に結果が返ってくるのはわかっていても、具体的にどうやって取り出すのかよくわからなかったです。また、request.results 配列に結果が返ってきますが、どれくらいのサイズがあるのかというと、999 もありました。ので、この例では、上位 3 つを取り出しています。
次は、画像を読み込む、textdetection で四角を切り取り、四角の中の文字を認識させるという流れになります。モデルを自作できるとよいのですが・・・
CoreML を使い始める。物体の写真を撮って、何が写っているか分類する。ネットに情報は色々あるけど、自分のやりたいことズバリに答えてくれる情報はない。しかも、Swift の情報ばかりで、Objective-C で書く人は完全にマイノリティ。しょうがないので、試行錯誤し、今日わかったのはこんなこと。
まず、モデルの指定方法。VGG16 というモデルが Developer サイトからダウンロードできるので、このデータを使う。Xcode にファイルを追加し、プログラム上ではファイルのパスを指定しモデルのポインタを生成する。ファイルパスの指定でつまずいてしまった・・・まず、ファイルの拡張子は.mlmodel となっているので、ofType で指定してやると見つけられないとのエラー。具体的にはこんな感じ。
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[NSString stringWithUTF8String:]: NULL cString’
tableView の一覧表示で、表示する項目は Storyboard の Prototype Cells 部分に UILabel を配置し、configureCell method で表示する内容を指定する。
Storyboard で UILabel を配置するとき、tag のデフォルトは 0 なんだけど 0 のままだとうまく表示がされない。デフォルトで entryDate なんて書いておくと、そのまま entryDate と表示されてしまったりする。なんじゃこりゃ。
こんな時は、tag の番号を 1 とかにしてやるとうまく表示されるようになる。0 は予約されているのかね。configureCell に書く内容は例えば次のような形。
- (void)configureCell:(UITableViewCell *)cell withEvent:(Brand *) item
{
// cell.textLabel.text = event.timestamp.description;
UILabel* entryDate = (UILabel*)[cell viewWithTag:1];
entryDate.text = [NSString stringWithString:item.entryDate.description];
}
ちなみに、Storyboard で UILabel を配置したからといって、outlet として接続すると error になったりしてしまいます。prototype cells はちょっと特別なんだなと。
tableView で、セルをタップしても detail に遷移しない。これ、基本的なことだろ?どういうことだ? prepareForSegue はちゃんと実装しているぞ?
理由は、storyboard で、detail へ segue の繋ぎ方を間違えていた。画面全体から繋いではダメで、xxxCell と書かれたのから detail へ繋がないとダメだと。そうしないと prepareForSegue は呼ばれないと。
色々めんどーなことがあるものだ・・・
久しぶりに自分のプログラムを書ける。二ヶ月ぶりぐらいか?前どこでハマっていたのかわかんなくなっちゃった。とりあえず build して状況確認。ああ、そうだ、tableView で + ボタンを押しても record が追加されていない、というか、再度起動すると追加されているのですが、+ ボタンを押した直後、一覧画面にぴょこっと追加されていないぞ?というとき。
ハマりましたね・・・tab で 3 画面作っていて 1 画面だけに起こる現象なのでなんかおかしいぞと。他の画面と見比べれば良いのですが、めんどくセーと思いつつ丹念に見比べました。
結局のところ、NSFetchedResultsControllerDelegate のdelegate method を実装したらちゃんと表示されました。具体的には、以下の通り。
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
switch( type )
{
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
default:
return;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] withEvent:anObject];
break;
case NSFetchedResultsChangeMove:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] withEvent:anObject];
[tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
break;
}
}
特に、
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
が重要で、CoreData に変更があったとき、tableView に反映するため呼ばれるのだと。よく似たその上の method は、section の反映。
やっぱり CoreData は鬼門。