You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.
TODOアプリを作ってみようシリーズの第4回目の演習課題その1です。
内容
#4 に引き続き、この回ではTODOの締切日時を入力できるようにし、一覧でも表示できるようにします。
目的
教材
スタート地点
前回のゴール地点であるブランチ write-to-userdefauls からスタートします。
ゴール地点
ブランチ add-deadline にチェックアウトすると完成した様子を見ることができます。
アプリの仕様
このissueで取り組む改善では以下の仕様を追加します
画面のレギュレーションは以下のようになります。
TODO追加画面
TODO一覧画面のセル
実装の方針
今回の実装は、デザイン面での修正はもちろんですが、データ構造にも変化があります。
今までのTODOはTODO本文のみを管理していましたが、今回は本文に加えて締切日時も加わります。
データ構造の変化がある場合は様々な箇所を変更していく必要があります。その点を留意して、デザイン面での修正と、データ構造など内部的な修正に分けて修正していきます。
まず、TODO本文と締切日時の両方を扱う方法について検討しましょう。TODOを扱う箇所は現在のところ
があります。それに対して、TODOのデータ構造には以下のようなサンプルが挙げられます。
todoBodyList
とtodoDateList
があるようなイメージですNSDictionary *todo = @{@"body": 本文, @"date": 締切};
のようなイメージ(a) のケースでは NSArrayを重複して管理せねばならず、さらにTODOの要素が増えると様々な箇所で引数を追加したりプロパティを追加していく必要があり、将来的に煩雑になりそうです
(b) のケースでは将来的にTODOの要素が増えても引数を各箇所で増やしたりする必要はありませんが、どのキーにどのような型のオブジェクトが入っているかをObjective-Cでは保証できません。このタイプではUserDefaultsにそのまま保存することができます。
(c) 一番堅牢なデータ構造となりますが、新規クラスを作成する必要があるなど、少し修正する量が大きくなってしまいます。またNSUserDefaultsで読み書きできる形式への変換も行う必要があります。
これらのメリット、デメリットを勘案してどのデータ構造を採用するかを決めます。よく使うデータ構造の場合は(c)を採用するケースが多いと思いますが、今回はあまり大きくないアプリなのできちっと型で縛るメリットはあまり享受できそうにありません。そこで比較的簡易にデータを扱うことのできる (b) のNSDictionary で扱うデータ構造を採用したいと思います。
下位バージョンとのマイグレーション
iOSに限らず、Androidでも考慮しないといけない問題なのですが、端末内に保存しているデータ構造を変更する際はマイグレーションについて考えないといけません。
過去バージョンのデータを新バージョンのデータ構造で使えるようにバージョンアップしていくのがマイグレーションとなります。
今回UserDefaultsに保存しているTODOのデータ構造は
となるので、旧バージョンのデータ構造のまま新バージョンで起動すると以下のようにクラッシュが発生します。
このような問題に対処するには、バージョンアップ時に保存しているデータ構造を更新処理を行います。
バージョンアップ時に呼ばれるコールバック関数などはないため、バージョンアップ済みかどうかをチェックして必要ならマイグレーションを行います。
一般的には起動後すぐに実行されるメソッドで記述することが多く、UIApplicationDelegateの
- application:didFinishLaunchingWithOptions:
などで実行することが多いです。マイグレーションに時間のかかる場合は、別途マイグレーション専用の画面を用意してやる必要があります。
このissueではマイグレーションを実際には行いませんが、以下のような実装にすると良いでしょう。
実装の解説
AddTodoViewControllerにDatePickerを追加し、レイアウトを調整 d4f750e
まずは新規追加画面で締切日時を追加するためのパーツを追加してレイアウトを調整します。
日付の入力にはUIDatePickerを利用します。 UIDatePickerは時間を指定するためのコンポーネントでUIKitに含まれています。UIDatePickerは時間の選択だけですが親クラスのUIPickerViewを利用すると任意のデータを選択することができます。
このUIDatePickerをstoryboard上に追加し、Autolayoutの調整を行います。このUIDatePickerの高さは固定で162ptとなっています。
配置が完了したらこのUIDatePickerをAddTodoViewControllerのプロパティとして追加します。
キーボードが出てきた時にPickerが隠れないようにすることを忘れないでください。
ToDoのエンティティをNSStringからNSDictionaryに変更 2b2c580
ToDoをNSStringで扱っていた箇所をNSDictionaryで扱うように変更していきます。
以下の該当箇所を直します
- doneButtonTapped:
)- addTodoViewController:addTodoCompleted
)@property (strong, nonatomic) NSMutableArray *todo;
は変えなくてもよいtodo[index]
でアクセスするときに扱う型をNSDictionaryに変える- tableView:heightForRowAtIndexPath:
- tableView:cellForRowAtIndexPath:
またここで、先ほど追加したUIDatePickerから締切日時を取り出しますが、これは
datePicker.date
で取り出すことができます。戻り値はNSDateで、Objective-Cでよく使われる時刻のデータ型です。
TODOの締切を表示するようにした b39c70d
TodoTableViewCellに締切を表示するようにします。
TodoTableViewCell.xibに時間表示用のラベルを追加し、レギュレーション通りになるようにレイアウトを調整します。
レイアウトの調整が完了したら、その時間表示用のラベルに時間を代入します。
Objective-Cに限らず、時間を文字列にするには、いろいろな表現方法があります。西暦、和暦、日付のみなのか、秒単位まで表示するのか、タイムゾーンはどうするのか、などさまざまな条件があります。
Objective-CではNSDateFormatter というクラスを用いてNSDateから日付の時刻を取り出します。
リファレンス → NSDateFormatter Class Reference
The text was updated successfully, but these errors were encountered: