2012年11月17日土曜日

iOSの日付処理まとめ

日付処理をするとき、どーだったっけ.. と迷うので私なりにまとめ。

日付関連の要素


NSCalendar

[NSCalendar currentCalendar]
[NSCalendar autoupdatingCurrentCalendar]

OSの設定 > 一般 > 言語環境 > カレンダー によって変わる。
「和暦」になっていると、NSJapaneseCalendar
「西暦」になっていると、NSGregorianCalendar 

Calendarには、後述の TimeZoneやLocale を保持しています。
autoupdatingCurrentCalendar は、ユーザが変更した内容を自動的に反映してくれるカレンダー。
クラス変数としてカレンダーを保持する場合などはこちらを使用するとよいかも。
だけど、保持せず使うタイミングで取得するのであれば、[NSCalendar currentCalendar] のままでよさそう。

NSDateFormatterで "yyyy-MM-dd" のようなフォーマットを使用している場合には、OSのカレンダー設定で和暦の設定されている場合に注意。

常に西暦で処理したい場合には、
[[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar]
NGregorianCalendarを作成して、NSDateFormatterなどに設定して使用します。

NSTimeZone

[NSTimeZone defaultTimeZone]
[NSTimeZone systemTimeZone]
[NSTimeZone localTimeZone]

OSの設定 > 一般 > 日付と時刻 > 時間帯 によって変わる。

時差を管理するもの。これは分かりやすい。
日本だったら、GMT +0900 / JST
NSDateFormatter などを init すると、OSの設定が入ります。

基本、あまり入れ替えることはないはず。あるとしたら、各国での時間を表示するときぐらいかな?
アプリ内部で日時情報を保持する場合には、
[NSDate timeIntervalSince1970] のGMT +0000 での経過時間で管理して、表示時にOS設定のCalendar, TimeZoneを使って 年月時間を表記 というのが素直な方法。

TimeZoneにはDaylightSavingTime(サマータイム)の情報も含みます。

NSLocale

[NSLocale currentLocale]
[NSLocale systemLocale]
[NSLocale autoUpdatingCurrentLocale]

OSの設定 > 一般 > 言語環境 > 書式 
OSの設定 > 一般 > 日付と時刻 > 24時間表示
などによって変わる。

国毎に変わるもの(フォーマットなど)を管理。
日本 : 「11月」 アメリカ:「November」
日本 : 「午前」  アメリカ : 「AM」
とかお金の単位とか文字が変わるのもあるし、文章の方向とか、カンマ と ピリオドの扱いとか国によって違うものが管理されています。

全世界で使われるようなアプリでは、それらの差を自分で書かないといけない... ところをLocaleを設定してFormatterに処理を任せるようにすれば対応できてしまう、本当はありがたいもの。ハマる所でもありますが。

逆に、ここは常に英語の表記したいのになーー  という場合には
[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]
と自分でLocale作成して設定もできます。

cunnretLocaleはユーザがOSの設定でしている情報。
systemLocaleは端末でフォルトの情報。

NSDate

GMTの経過時間(NSTimerInterval)を保持している。
Calendarなどの概念が含んでいないのでtimezoneが違っても同じものとして扱える。
内部で保持するデータモデル的クラスです。
UI上に表示する場合には、NSDate と、上記Calendarなどを組み合わせて日時をフォーマットして表示する感じ。


日付を扱うクラス


NSDateFormatter

@"yyyy-MM-dd HH:mm:ss" でフォーマットを指定しているのに、

「0024-11-16 21:09:17」になっちゃった!
(カレンダーに和暦が設定されているとき)

「2012-11-17 午後11:07:47」 になっちゃった!
(24時間表示の設定がOFFのとき)

とういのが、あるある 事件。
カレンダーについてはNSDateFormatterにNSGregorianCalendarを設定して、
24表示については、Localeに[NSLocale systemLocale]をすると上手くいきます。

systemLocaleにすると24時間表示になる理由はわからず。。
どのパラメータが影響しているのだろうかー。

OSで設定されている日付フォーマットに沿って文字列を生成するには
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle: NSDateFormatterShortStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
NSString *dateStr = [formatter stringFromDate:date];
のようにします。
日付部分のフォーマット、時間部分のフォーマット を CFDateFormatterStyle の定数で指定します。CFDateFormatterNoStyle を指定すると表示なしになるので時間だけを表示ということも可能。

NSDateComponent

NSCalendar と NSDate から、年、月、日、時間 などの取得や、日付計算ができる。

NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponent *comp = [calendar components:params fromDate:[NSDate date]];

本日の00:00:00のNSDateを取得
NSDateComponent *comp = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:[NSDate date]];
NSDate *date = [calendar dateFromComponents:comp];

1日後のNSDateを取得
NSDateComponent *comp = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:[NSDate date]];
comp.day += 1;
NSDate *date = [calendar dateFromComponents:comp];

2つのNSDateの日数を取得
NSDate *fromDate;
NSDate *toDate;
NSDateComponent *comp = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger between = comp.day;

曜日を取得
NSDateComponent *comp = [calendar components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
NSInteger weekday = comp.weekday;

曜日の文字列は、NSDateFormatter の weekdaySymbols から取得できるけど、曜日の並び順番がズレているので注意。

NSDatePicker

これに先日ハマっていた...

DatePickerにGrigorianCalendarを設定する

NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
datePicker.calendar = gregorian;

iOSの設定で、24表示無し(AM PM表示) にしているとき
iOS5(iPhone4S)の端末では、AM PM表示になるのに、
iOS6(iPhone5)の端末では、24時間表示になってしまう。

NSDateFormatter では Locale を SystemLocaleにすると24時間表示にできたが、
DatePickerのLocale、設定するCalendarのLocale ともにSystemLocaleにしても、24時間表示にはならない。

端末設定に依存せず、常に24時間表示にする方法はわからず。

NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
gregorian.locale = [NSLocale currentLocale];
datePicker.calendar = gregorian;

...と 自作したGregorianカレンダーのLocaleにCurrentLocaleを設定すると、iOS5,6ともに設定された24時間表示どおりになるようです。

• • •

2012年11月4日日曜日

CoCafe ver2.0 リリース!



CoCafeがVer2.0になりました!

CoCafeは、GooglePlaceAPI(Googleローカル)を使っているのですが、このAPIはGoogleマップ上に取得結果を表示しないといけない という規約があります。

ご存知の通り、iOS6からはGoogleマップではなく、Appleのマップに代わり規約違反状態に....
これはマズイ!マズイ! ということで、Googleマップでの表示になるように修正を加えるとともにUIも一新しました。

Googleマップを表示する方法としては、
WebViewをつかってWeb表示にする という方法と、
地図の画像を取得してきてiOSのMapKit地図上にタイルとしてペタペタ貼るという方法が考えるのですが、後者も規約違反らしい。

Google Maps/Google Earth APIs Terms of Service より
10.1.1. General Restrictions. 
(a) No Access to Maps API(s) except through the Service. You must not access or use the Maps API(s) or any Content through any technology or means other than those provided in the Service, or through other explicitly authorized means Google may designate. For example, you must not access map tiles or imagery through interfaces or channels (including undocumented Google interfaces) other than the Maps API(s).

ということで、WebViewと使って今までと同じ機能になるように @shin1ogawaの協力も得つつ仕上がりました!

WebViewなので表示にもっさり感。でもGoogleマップの安心感があります!w
AppleStoreにあるGoogleマップではさくさく動くのがあるのだけど、タイル使ってるんじゃないかなぁーという密かな疑惑中...

iOS5ではネイティブマップ(Googleマップ)を使い、
iOS6以上ではWebViewでマップを使うようになっています。
やっぱり、ネイティブマップの方がサクサク動くのでiOS5はネイティブの実装で。

もう一つの大きな変更がUIの変更。
今までのものは、検索結果のマップ表示 と 一覧リスト を切り替えで表示 する仕組みでしたが、これだと、
「一覧リストでみたお店をマップのどこにあるかを表示」という事ができなくて不便でした。
これを満たすためには、マップ表示と、一覧表示を同じ画面に表示する必要があるなーーということで...
こんな感じで、マップをスライドさせて一覧が見れるようにしました。
行を選択すると、マップ上で選択したお店が中心に移動してお店名の吹き出しがでます。
ボタン類の所を、左右にドラッグするとマップがスライドするように出来ているのと、お店の詳細画面も左から右へのスワイプでマップに戻るように出来ているので、
検索 → 一覧を見る → マップ上で場所確認
のようなよく行う事が片手でも出来るように工夫しています。

FIND の 下の真ん中にある
のボタンは、表示中マップの中心から再検索 というボタンです。
マップをズイズイと移動したら、このボタンタップで再検索してみてくださいね!

次はコンパス機能を使って、どっちの方向を向いているか?
を実装できたらなぁーー とか思っています。



CoCafe
価格 : 無料 (広告非表示は250円)
カテゴリ : ナビゲーション
対応機種 : iPhone
対応OS : iOS5.0以上

• • •