CS193p 課題4の内容

CS193pのクラスの公式サイトにLecture 9の課題としてAssignment4.pdfがアップされていますので、日本語にしておきます。いつものようにヒントにいくつか有益なことが書いてあります。

そのヒントにいつも書いてる、目安のコード量がとても少ないことに今までショックを受けていました。なにせ自分のコード量の1/3とか1/4だったので。それが今回のヒントでは、「括弧を除いて、追加された実行される行を数えたら」という但し書きがついています。あー、なるほど、それならそんなもんかも。少し安心しましたが、これまでの講義のデモを見るといろんなところで記述量が少なくなる工夫がされていて参考になり、自分のコードがイマイチなのがよく分かります。課題は毎回100ステップ程度が目安になっていますが、例えば誤差を+ー10ステップと考えてもその最大誤差の20ステップ違うコード同士見比べると見た目がかなり違っていることでしょう。今回のヒントにも書いてありますが、力ずくでやる前にコード量が少なくなるように考えてなるべく工夫したいものです。

課題の内容

作成するアプリ

Flickerの人気のある写真スポットの一覧を表示するアプリを作成する。

ゴール

テーブルビュー、タブバーコントローラ、スクロールビュー、イメージビューに慣れ親しみ、より多くのMVCを使ったアプリを作る経験を積むこと。

事前準備

Flickr API キーが必要。無料のFlickrアカウントで十分。(写真のポストはせずに、単に写真を取得するだけ)

Required Task

1. 与えられたFlickrFetcherクラスメソッド topPlacesを使って、Flickrで人気のある写真撮影スポットの配列を取得する。それはそれぞれの場所の情報持ったNSDictonaryの配列である。

2. UITabBarControllerベースの2つのタブがあるユーザインタフェイスを作る。最初のタブには、アルファベット順でRequrired Task #1で取得した写真の一覧をUITableViewで表示する。2つ目には、直近に見られた20枚の写真のリストをUITableViewで表示する。

3. アプリ内のテーブルビューで表示される全ての場所は、セルのタイトルが最も詳細な場所になっていて、サブタイトルには、例えばstate,province,countryなどその他の場所情報が表示されること。

4. ユーザがRequired Task #1のリストから場所を選択したら、Flickrからその場所の最新の写真を50枚取得してリストで表示する。これをするのにFlickrFetcherのphotosInPlace:maxResults:を使う。それは写真に関する情報を含んだdictionaryの配列を返す。

5. 全ての写真のリストは写真のタイトルをテープルビューのタイトルとして、そのdescriptionをサブタイトルとして表示する。写真にタイトルがない場合は、descriptionをタイトルにする。どちらもない場合は、“Unknown”をタイトルにする。

6. ユーザがリストから写真を選択したら、スクロールビューの中に画像を表示して、パンとズームで(手頃な範囲で)操作が出来るようにすること。Flickrの写真のURLはurlForPhoto:format:を使って取得する。

7. 写真のタイトルは、ユーザが画像を表示している時には必ず画面のどこかに表示すること。

8. 写真の画像が画面に表示される時は最初になるべく余白がないように可能な限り写真全体が入るようにズームして表示すること。ユーザがデバイスを回転させたりピンチで写真をズームイン/アウトした時には、それを続ける必要はない。

9. 作成するアプリケーションは、iPhoneで縦向きでも横向きでも動作すること。iPadのサポートはオプショナルとする。(しかし、次の週には必要になるので、今のうちに実装しておいてもよい)プラットフォームにふさわしいユーザインタフェイスを使うこと。(例えば、iPhone上に情報を表示するUINavicationControllersを使うこと)

10. 直近のタブには、直近に閲覧された写真の一覧を重複無しで日時降順に表示すること。画面に表示する毎に(例えば、viewWillAppear:の中で)リストを更新すればよい。写真はdictionaryの中に登録された“id”でユニークに識別される。

11. 直近の写真のリストは、NSUserDefaultsに保存すること。FlickerFetcherメソッドからの戻り値の配列は、全てプロパティリストである。

 

ヒント

1. FlickrAPIKey.hに自分のFlickr API Keyを設定すること。そうしないと動かない。

2. この課題はTabbed Application テンプレートで始めればよい。(もしくは、Master-Detai Applicationテンプレート)しかし、Single View Applicationテンプレートで始めて、UITableViewContollersの中に必要なものをドラッグしてEmbedメニューを使ってUINavigationControllers, UITabBarContollersやUIScrollViewsでEmbedして、それからctrl-ドラッグで(rootViewControllerやViewControllersのなどと)接続したりSegueをセットしてもよい。この課題の重要な部分は、ストーリーボードで作るオブジェクトがそれぞれどう関係しているのかということの理解を深めることである。

3. FlickrFetchrのコードをアプリ内にコピーしてAPIキーをセットしてから最初にすることは、topPlacesの問い合わせをして、NSlogでその結果を表示することである。これで取得したFlickrの結果のフォーマットがわかる。(それはNSDictionaryオブジェクトの配列になっている。)特定の場所の写真のリストのFlickr問い合わせについても同じように行う。

4. 注意深く見れば、Flickrから取得した写真の情報のキーdescriptionの値は実際の写真のdescriptionではないことに気付くだろう。その代わりに、その中にはキーに _contentを持った別のNSDictionaryがある。それが実際のdescriptionである。valueForKeyPath:メソッドは、キーとドットがを使って“description._content”という形式でsub-dictionaryにアクセスする。

5. (写真のdictionaryにある)キーのidはユニークで、永続的な写真の識別子である。

6. テーブルビューベースのMVCを作るためには、オブジェクトライブラリからTable View Controllerをストーリーボードにドラッグアウトして、クラスをUITableViewCOntrollerのカスタムサブクラスにする。(新規ファイルを作成する時にスーパークラスをUITableViewControllerに変更するのを忘れずに)この課題ではいくつかのUItableViewContollerのサブクラスが必要になるだろう。

7. 各MVCは、プッシュされる前に必要情報がセットされてそれから何かをするようにするべきである。そして、可能な限りコードが再利用されるようにオブジェクト指向で考えること。このアプリの多くのMVCはとても良く似ている。何かをするためにUITableViewControllerのサブクラスを作成して、それから別のことをするのにそのサブクラスを作って少し洗練させるやり方はすごく良い。

8. この課題の全てのUITableViewCellsはサブタイトルが必要なことに気をつけて、Xcodeのdynamic prototypesの中でセルのタイプをそのようにセットすること。単に一貫性の問題だが、SubtitleタイプにするためにTableView:CellForRowAtIndexPath:の中に"backup"セルを作成するコードを記述するのも同様に良い考えだ。

9. Xcodeで指定してたUITableViewCellの再利用識別子とtableView:cellForAtIndexPath:メソッドの中のものと一致させるのを忘れないこと。これはUITableViewControllerのサブクラスのサブクラスを持つことを選択した場合に(tableView:cellForRowAtIndexPath:を継承することになるので)少し混乱する可能性がある。だから、再利用識別子には良い名前を選ぶこと。(それは、セルが表示しているものを簡潔に表している)

10. インターネットのURLをUIImageに変えるのは簡単である。単にそのURLのNSDataを作って([NSData dataWithContentsOfURL: そのURL])、それからそのNSDataを使ってUIImageを作ればよい。([UIImage imageWithData:imageData])

11. Required Task #8(写真をズームして初期表示)は、UIScrollViewのboundsと写真のサイズを含む計算が必要になる。講義であった、(View Controllerのライフサイクルの中で)ビューの幾何学計算はどこで行うべきかを忘れずに。

12. 今週iPadをサポートする場合は、UISplitViewContollerDelegateをサービスするためのビューコントローラがいくつか必要になる。ディテールビューコントローラで行えば、行き来するバーのボタンは簡単に付けられるだろう。(UIにボタンを配置するのはそのビューコントローラなので)

13. iPadでは、Segueよりも、Split Viewのマスタビューにあるテーブルビューコントローラからディテールビューコントローラを更新する場合、マスタの中でtableView:didSelectRowAtIndexPath:メソッドを実装したくなるだろう。(それはテーブルビューのターゲット/アクションのようなもの)そして、prepareForSegue:sender:で行っていたことをそのメソッドの中でしたくなるだろう。(例えば、遷移先のビューコントローラのモデルや表示方法のプロパティをセットするなど)

14. 画像表示MVCの画像を再設定する場合(例えば、Split Viewの詳細ビューコントローラの場合)新しい画像のcontentSizeをリセットする前にzoomScaleを1に戻すのを忘れないように。zoomScaleはcontentSizeに影響を及ぼす(例えば、ズームインしている時にはcontentSizeは自動的に大きくなり、ズームアウトしている時には小さくなる)ので、zoomScaleが1以外でcontentSizeがおかしくなった状態でスタートしたら、期待した結果にならないだろう。

15. NSArrayのmutableCopyメソッドは、NSUserDefaultsに変更不可能な状態で格納されたデータに変更を加えたい時に役に立つだろう。

16. アプリのレスポンスが悪いことに気付くだろう。Flickrに問い合わせをしている間はいつも長い停止状態になるだろう。これはとても良くないが、次の週で解決方法を学ぶので、今はその問題を修正しようと時間を使わないこと。

17. いつものように、コードの量は多くなく括弧の間の行を数えれば100行以下である。1つの機能にかなり多くの行のコードが必要なことに気が付いたら、他にもっといい方法がある。一般的に、"brawn over brains"ソリューション(例えば動くまでひたすらコードをタイプし続ける)は、バグを生みやすく、保守しにくい。だから、それを避けよう!そういう理由で我々はオブジェクト指向を使っている。そのメカニズムを十分に使いなさい。

コメントをお書きください

コメント: 5
  • #1

    maroso (金曜日, 02 3月 2012 17:31)

    最近この講義を発見して追いかけていまして、大変参考にさせていただいております。

    さて、Required Task 8. の解釈ですが、「余白のできない範囲で写真より多くの部分を表示」と受け取りましたがいかがでしょう。絶対の自信はありませんが。
    もし上の解釈が正しいとすれば、zoomToRect:animated: を使うのではなく、scrollView.bounds.size と 写真のimage.size の縦横それぞれの比率の大きい方を採用することに
    なるかと思います。
    これは Hints 11. の記述とも符合するのではないでしょうか。

    いきなりの指摘で大変失礼しました。
    参考になる記事をありがとうございます。重ねてお礼申し上げます。

  • #2

    good-morning-call (金曜日, 02 3月 2012 18:37)

    コメントありがとうございます!
    同じ講義をみて同じように課題をやっている人がいるのがわかってとても嬉しいです。
    この部分の実装はかなり苦労したので今でもよく覚えています。
    解釈は、私もmarosoさんと同じような解釈をしているつもりです。文章の書き方がまずくて微妙なニュアンスが伝わっていなければすみません。
    縦横比を判断基準にするのは、おっしゃる通りで、私もかなり長い時間考えてその方法にたどり着きました。すぐにそれに気が付かれたのは凄いと思います。
    zoomToRect:animated:を私は使って実装しましたが、それは、とても小さいサイズの写真を表示してものすごく余白ができてしまうときにズームさせるためです。iPadで実装すると画面が大きいのでよく分かると思います。しかしながら、私は、iOSアプリ開発の初心者で、この講義を頼りに試行錯誤しながら実装していましたので、私が実現した方法以外にももっと良いやり方はたくさんあると思います。

  • #3

    maroso (金曜日, 02 3月 2012 21:01)

    さっそくのお返事ありがとうございます。

    私も iOS の初心者です。まだ iOS device は一台も所有していません。
    それにしてもこの講義は素晴らしいですね。これが無料とは申し訳ないです。

    さて、プログラミングの問題ではなく英語の解釈に関わることだと思われますのであまり突き詰めても仕方がないかもしれませんが、少し解釈が違っているように感じます。

    一言でいうと、写真のより多く部分を初期表示ことと余白ができないことのどちらを優先するかということです。私は後者が優先すると解釈しました。したがって次のポスト「Flickr人気スポットアプリができた!」の三番目の写真は天地の余白がなくなるまで拡大され、その結果右側が表示範囲からはみ出した状態が初期表示になると思います。いかがでしょうか。

    私がはまったのは、ヒントの14をちゃんと読んでいなかったため、splitViewの詳細ビューコントローラーの際に、scrollView.contentSize に写真のサイズを設定する前に zoomScale を1 にしなかったために初期表示倍率が期待した結果ならなかったことです。iPhoneではあたらしい写真を表示するたびにビューコントローラーが instantiate されるため必要なかったんですね。

  • #4

    good-morning-call (土曜日, 03 3月 2012 12:01)

    なるほど、少し解釈が違いますね。
    該当する原文はこれです。
    it should initially be zoomed to show as much of the photo as possible with no extra, unused space.
    私は、extraという単語に着目して解釈しました。
    その結果、「余分な余白がないように」となるので、「余分な」という言葉の意味合いから余白がないことの優先順位よりも、写真のより多くの部分を表示することを優先させるように解釈しました。
    この解釈の違いで、余白付きで写真全体が表示されるのと、余白なしで写真の一部が画面全体に表示されるという結果の違いになるのですね。
    結果の違いを見ると、masaroさんがおっしゃるように、余白なしで写真の一部が画面全体に表示されるようにするのが正解のような気がします。
    ご指摘ありがとうございます。

    ところで、iOS deviceを持っておられないということですので、少し先のことをお伝えします。
    私もこのすばらしい講義をきっかけにiOSアプリ開発者の権利とMacアプリ開発者の権利の両方を1年間(計200ドル)購入しました。そして、結果的には自分の作りたいと思ったアプリを作ることができるようになりました。参考書はこの講義とAppleの公式ドキュメントだけです。
    しかし、iCloudでデータを共有する段階になって、iOSとOS X ではまだクラスの互換性が確保されていないことがAppleに問い合わせて分かりました。(この講義で使われているUIManagedDocumentを使うとiCloudでiOSとOS XでCoreDataのデータ共有ができません)そして、このようなドキュメントを読んだだけでは解決できないことを問い合わせをするのには1回あたり50ドルの費用を払わないと一切質問に答えてくれません。例えそれがバグに関することでも同じです。つまり、この講義はすばらしく無料なのが不思議なくらいですが、いったん開発者になるとその先は茨の道で費用も時間もかかりますので、それなりの覚悟が必要です。
    この講義が素晴らしいのは、インストラクターのPaul Hegarty氏の個人的な資質によるもので、氏はAppleのエンジニアだと思いますが、だからといってアップルの開発者に対するサポートが素晴らしいということにはならないので、ご注意なさってください。

  • #5

    maroso (日曜日, 04 3月 2012 23:47)

    情報ありがとうございます。

    まだCore Data や iCloud まで進んでいないのでおっしゃることの内容は理解できませんが、質問に料金がかかるということは了解しました。

    しかし考えてみますと、フルセットの開発環境 Xcode が無料で利用でできることはありがたいです。

    Windows の世界ですと、そもそも開発環境を購入する必要がありますし、ほぼ必須のMSDNの購読も個人ではちょっと勇気のいる金額です。(Visual Studio Professional with MSDN 年間173,250円 テクニカルサポート2インシデント含む)

    どちらにしても覚悟がいるということは確かのようですね。