2009/11/20

Objective-Cの日付文字列をNSDateに変換

DBから出てきた日付は文字列なので、NSDateFormatterクラスのdateFormatterメソッドを使ってNSDate形式に変換します。

//日付の変換
NSString *datestr = [setDataDict valueForKey:@"set_datetime"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterFullStyle];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
[NSDate *datetime = [dateFormatter dateFromString:datestr];
[dateFormatter release];
ここを参考に
http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
日本語はこっちね
http://e6sc8e.jugem.cc/?eid=413

2009/11/17

iPhone Navigation Bar + Tab Bar + hidesBottomBarWhenPushed

タブとナビゲーションビューを使っているとき、ナビゲーションで進んだ先ではタブバーを隠したいときに使えるポロパティにhidesBottomBarWhenPushedがある。文字通り別のビューがプッシュされたら下にあるバーを隠す設定をするためのプロパティ。アクセサを使ってこんな感じで設定する。
[SomeViewController setHidesBottomBarWhenPushed:YES];
[self.navigationController pushViewController:SomeViewController animated:YES];

はまりどころ。hidesBottomBarWhenPushedは呼び出す側のコントローラで設定しておかないと行けない。上の例でいうと。SomeViewControllerのdidViewLoadで設定することができるが、これだとちょっとよくない。
didViewLoadで設定するとSomViewControllerがPushされた場合、ボトムバーが消えない。SomeViewController以降のpushされたビューで初めてボトムバーが消える。(SomeViewControllerを再描写して初めて消えているっぽい)。多分didViewLoadでは1手遅いんだと思う。didViewLoadより前でhidesBottomBarWhenPushedを設定しないといけない。つまりSomeViewControllerが呼ばれた時点でバーを隠す場合は、上のサンプルコードのように呼び出し側で設定する。

2009/11/09

iPhone タップ/タッチのイベントを取得する

タッチ/タップのイベントを取得できるのはUIResponderをスーパークラスに持つクラス。UIResponderを親に持つクラスはレスポンだと呼ばれる。レスポンダはタッチ/タップのイベントを取得することができる。
UIViewはUIResponderを継承している。UIControlはUIViewのサブクラス。つまりInterface Builderで構築されるUIはすべてレスポンダ。イベントは一番手前(? ユーザー直面しているビュー)のレスポンダにまず渡される。イベントを渡されたビューが処理しない場合、次のレスポンダに渡されていき、レスポンダの階層をさかのぼっていく。適時イベントが処理される部分が現れると、イベントは処理され破棄されて、イベントが終了する。
ということらしい。
レスポンダに記述する、イベントを処理するメソッドはこんな感じ。UIEventとしてイベントが渡されている。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
 NSUInteger  numTaps = [[touches anyObject] tapCount];
 NSUInteger numTouches = [touches count];
 NSLog(@"タップの回数 : %d",numTaps);
 NSLog(@"タッチの数: %d",numTouches);
}

Interface Builderで作ったUIでは、イベントはそのビュー(UIButtonとか)でキャッチされて、IBActionとして指定したViewControllerのメソッドに処理がバイパスされていると考えるということかな

2009/11/06

iPhone NSTimer NSTimerを止める。

NSTimerを止めるとき、単に[timer release]だけではだめで、コンソールに下記のようなエラーがでる
malloc: *** error for object 0x3fe29f0: double free
*** set a breakpoint in malloc_error_break to debug

メインループがこの NSTimer を参照しているのが原因のようです。単純にタイマーを止めるには[timer invalidate]を使うようです。またNSTimerのインスタンスを作っている場合はこれを解放する必要があるので別途[timer release]としたほうが良さそうですが、これをやるとエラーが発生してプログラムが落ちます。

デベロッパドキュメントによると
NSTimer:invalidate:はメインループのNSRunLoopオブジェクトからTimerを解放するメソッドです。
このメソッドがNSRunLoopからタイマーを取り除く唯一の方法と書かれています。NSRunLoopから解放されて、さらにreleaseされると書かれています。下の例の用にNSTimerを変数として保持してない場合は、invalidateするだけでreleaseまで行われていると考えて良さそうです。releaseを実行すると落ちます。
- (void)viewDidLoad {
 [super viewDidLoad];
 count = 0;
 someTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateCount:) userInfo:nil repeats:YES];
}

- (void)updateCount:(NSTimer *)timer
{
 count++;
 NSString *str = [[NSString alloc] initWithFormat:@"Count %d",count];
 countLabel.text = str;
 [str release];
 if (count > 5) {
  [timer invalidate];
 }
}


NSTimerを呼び出すあたりが曖昧になっていると、結構はまる。NSTimerオブジェクトを以下のように呼び出した場合
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateCount:) userInfo:nil repeats:YES];
デベロッパドキュメントによると
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:はクラスメソッドで、その説明は
Returns a new NSTimer object, scheduled the current NSRunLoop object in the default mode
デフォルトモードが何かは無視して、このメソッドは既存のNSRunLoopにスケジュールされているNSTimerを返すということらしい。つまり生成したNSTimerオブジェクトの管理はNSRunLoopで行われていて、自分のコントローラ内で勝手に消すと怒られるということのよう。
viewDidLoadのような一回だけ呼ばれるメソッドでNSTimerを宣言するときはいいのですが、[self startTimer]とかして、何回でもそのメソッドが叩けるとその数だけNSTimerオブジェクトが生成されてしまう。NSTimerをいつどこで作るかはちゃんと考えてからやった方が良さそう。



ちなみにNSTimerのfireメソッドでスケジュールに無いタイミングでメソッドを実行できるらしい。

2009/11/05

iPhone JSON Framework使用時に実機でのコンパイルエラーの対処

iPhoneでJSON Frameworkを使って開発しているとき。シュミレータではエラーが出なかったのが、実機でのデバッグではコンパイルエラーになる場合の対処法。
Xcode3.1.4 + iPhone SDK 3.1.2 + iPhone 3G(OS 3.1.2[7D11]) + JSON Framework (2.2.2)で遭遇しました。
エラーメッセージ
Codesign error: 〜“object file format invalid or unsuitable”
Codesignのエラーということ、ググるとこれはJSON Frameworkが悪さしているということが分かった。

コンパイルエラーの対処方法
プロジェクト > プロジェクト設置の編集 > ビルド > コード署名リソース・ルールパス を編集
デフォルトでは空欄なので以下を追加
$(SDKROOT)/ResourceRules.plist

これでコンパイルは通るようになります。しかし実機での実行時に[NSObject JSONValue]メソッドが呼び出せないエラーが起こり例外エラーになってしまいます。この対処法がわからない。

JSON Frameworkの導入はここを参考に
http://www.syuhari.jp/blog/archives/1146


コンパイルエラーの対処法はここを参考にしました。
http://iphone.galloway.me.uk/2009/04/json-framework-codesign-object-file-format-invalid-or-unsuitable/

解決
JSON Frameworkを使うときに[NSSring JSONValue]が呼べない問題は
//ここでJSONValueを使うのはだめ,実機で落ちる
//NSDictionary* jsonItem = [jsonStr JSONValue];

//こっちを使う エラーはセットしておく方が大人の対応。
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
NSDictionary *jsonItem = [json objectWithString:jsonStr error:&error];
if (!jsonItem) {
 NSLog(@"%@", error);
}

JSON Framework - Google Codeのフォーラムで議論されてました。
http://code.google.com/p/json-framework/issues/detail?id=22

2009/11/02

Objective-Cでの数値→文字列とその逆.

Objective-CでのInt型→NSString型とその逆の渡し方。
特に難しいことはなく、intValueメソッド、またはstringValueメソッドを使えばキャストした値を返してくれる。

//NSSTring型をInt型に
int intTime = [ @"20" intValue ];

//Int型を文字列に
NSString* strTime;
strTime = [ NSString stringWithFormat : @"%d", 20 ];