Flickr人気スポットアプリができた!

CS193pのLecture 9の課題4になっているアプリが出来ました。出来た後でテストをかねて色々なFlickrの写真を見ていると、東京モーターショーの様子やら海外のいろんな様子など世界中の今撮られた写真を見ることが出来て、とても楽しいアプリになりました!

iPhone

iPad

 

閲覧履歴の一覧を表示するときに、写真をアイコン表示してみました。

しかし、写真の取得に時間がかかって堪え難いくらい遅いです…。

ヒントに次の講義でレスポンスの改善を行うと書いてあったので、次の講義が楽しみです。

 

はまったところ

データのだぶり

ヒントを見ながら最初の一覧画面を作ってみると…

うわー、だぶりまくり!

データの単位は写真のようで、自分で場所単位に圧縮する必要がありました。

ユニークにするにはNSMutableSetに場所名を放り込んで行けば勝手に出来てそうだけど、他のいろんな場所の情報も必要になるので、NSMutableDictionaryにキーを場所名にして場所情報と一緒にぽんぽんと放り込んでDictionaryさんにユニークにしてもらいました。

そして、出来たdictionaryのキーの部分を取り出せば配列になっているので、あとはその配列をソートするだけ、簡単です。

文字列のソート

が、しかし、配列のソートってどうやるんだろう???

NSMutableArrayのリファレンスをみるとsort〜というメソッドがいくつかあるけれど、使い方がわかんない…。

グーグルさんに聞いてみると、教えてくれている人がいました。

あかばね式[iPhone]objective-c で文字列をソートする

iPhone SDKの話。
文字列が入った配列をソートするには、例えば以下のようにする。

NSMutableArray* strings = [[NSMutableArray alloc]

                          initWithObjects:@"aaa", @"ccc", @"bbb", nil];
[strings sortUsingSelector: @selector(compare:)];
NSLog([strings description]);

@selector に対して、compare: を渡すこと。
コロンを忘れないように。
コロンを忘れてもコンパイル時にはエラーも警告も出ないのだ。

 

もう少し調べてみると、このcompare: というのはNSStringのメソッドで、NSStringのリファレンスをみると他にもここで渡せるメソッドが用意されていました。

  • - caseInsensitiveCompare: 大文字小文字を区別せずに比較
  • - localizedCompare: ローカライズされた文字列の比較
  • - localizedCaseInsensitiveCompare:  上記を大文字小文字区別せずに比較

compare:でよさそうだけど、念のためにlocalizedCaseInsensitiveCompare:でソートしました。

今回はたまたま昇順なのでこの方法で一発でソートできたんですが、降順だと少しややこしそう。一発で出来る方法がなさそうで、一番簡単そうなのはNSArrayのreverseObjectEnumerator: メソッドを使って逆順でデータを取得する方法のようです。

もしくは、テーブルビュー限定であれば、UITableViewControllerの中のデータを返すdelegateメソッドの中で、indexPath.rowをそのまま使うのではなくて、

( レコード数 − indexPath.row − 1 )

をデータ配列のインデクスに使ってデータを取得して返してあげればいいのかな。

写真を画面枠に合わせて表示(最初に表示された時だけ)

画面枠にあわせてなるべく写真全部が表示させるようにするのは、ZoomToRectが勝手にやってくれるので楽なのですが、ピンチでズームさせたりした後に回転した時はそうせずにその時点のズームスケールで表示させるのがなかなか大変でした。

最初にiPhone版を作る時に試行錯誤して、出来た後にiPad版を作っている時にもはまりました。それは、iPad版ではView Controllerを作り直したので、scrollVIewへのアウトレットの接続を忘れていて(ImageVIewへのアウトレットの接続をやって満足していた)のと、struts and springsの設定を忘れていたせいです。どちらも散々講義の中で言われていたことですけどね…。

満足できる動きになったやり方です。

1. struts and springs を ImageView, ScrollView共に次のように設定する。

2. 次のようにView Controllerのライルサイクルメソッド内で処理

VCライフサイクルメソッド 処理内容
- (void)viewDidLoad
  • ImageViewに画像をセット
-(void)viewWillAppear:
  • ZoomToRect:で画面に収まるようにズーム(サイズは元画像のサイズを設定)
-(void)viewWillLayoutSubviews
  • ImageViewのフレームサイズを現在のズームスケールの画像サイズに設定

タブバーのタブ名の設定

タブの名前を設定するのにかなりstoryboardをあちこち探しました。

タブバーの部分をクリックして選択してから、Attributes InspectorのTitleでタブの名前が設定出来ます。

Identifierでシステム提供のアイコンを選ぶことが出来ます。

NSUserDefaultsにはプロパティリストしか保存できない

ヒントにオブジェクト指向で考えて作るようにとあったので、保存するデータを最小限にしようと最低限必要な写真の情報だけのクラスを作ってそれを保存しようとしましたが、クラッシュしました。

NSUserDefaultsにはプロパティリストしか保存できず、つまり、プロパティリストとして認識出来るデータタイプのデータしか保存出来ないということでした。

そして、その認識できるデータタイプとはこれです。

  • NSData
  • NSString
  • NSNumber
  • NSDate
  • NSArray
  • NSDictionary

配列に入れてるから大丈夫だろうと思いきや、配列とDictionaryは、中に入っているデータもこのうちのいずれかでないといけないとエラーログに言われました(涙)

キーと文字列のセットでNSDictionaryにして保存するのが良さそうですが、結局元のデータがそうなっているので、元のデータをそのまま保存することにしました。

いらない情報がたくさん含まれていてそれをそのまま保存するのは気が引けるのですが、将来の拡張性を考慮したということにしておきました。

テーブルデータの最新表示

ユーザデフォルトから取得した直近の閲覧履歴を一覧表示する時に、アプリ起動時のみ正常に動くのですが、そのあとはいくら写真をみても更新されません。ユーザデフォルトの使い方が悪いのかと散々悩みましたが、UITableViewを再表示しないといけないことに気が付きました。UITableViewはずっとバッファを使って表示してるんですね!

具体的には、データをセットした後に次のコードを実行するようにしたら上手く動きました。

    [self.tableView reloadData];

<前の記事 

CS193p 課題4の内容   - CS193p - Lecture 10 次の記事>