2009年5月19日火曜日

UINavigationの片方のサイドに複数個のボタンを持たせたい



こんな感じでUINavigationBarの片方のサイドに複数個のボタンをおく方法を探してみました。UIToolBarと異なり、UINavigationBarではUINavigationItem.rightBarButtonItem, UINavigationItem.leftBarButtonItem, それからタイトル部分と、最大でも3個しかアイテムを配置することが出来ません。そのため、複数のボタンを一つの配置箇所にまとめて配置したい場合には、カスタムビューを作成する必要があります。
Appleの配布しているデモアプリケーションにもありますが、こういう場合にはUISegmentedControlのmomentaryプロパティをYESに指定して、ボタンみたいに利用するのがいちばん良いようです。
NSArray *items = [NSArray arrayWithObjects:@"Add", @"YourTurn", nil];
UISegmentedControl *segmentedControl = [[[UISegmentedControl alloc] initWithItems:items] autorelease];
segmentedControl.selectedSegmentIndex = UISegmentedControlNoSegment;
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
[segmentedControl addTarget:self action:@selector(segmentedControlClicked:) forControlEvents:UIControlEventValueChanged];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:segmentedControl] autorelease];

この方法でひとまずはUINavigationItemの右側に複数のボタンを作ることが出来ました。しかしまだまだ完璧ではなくて、現地点で分かっている限りでも二つの問題があります。
  1. ボタンの背景色が通常のボタンとは異なる色になる
  2. 個々のボタンひとつひとつを個別にDisableすることができない

Appigo Todoなんかは普通にUINavigationBarの右側に3つもまとめてボタンを綺麗に配置してあったりするのですが、一体全体どうやってるんでしょうね?・・・うーん。

2009年5月7日木曜日

Objective-CではvalueForKeyPath:で集計関数みたいなものが使えて凄く便利

Pythonだと、
list = [{'no':1, 'name':'akisute'}, {'no':2, 'name':'abesi'}, {'no':3, 'name':'hidebu'}]
maxNo = max(list, key=lambda x:x['no'])
こんな感じでリストに含まれるオブジェクトの最大値を簡単に取り出せたりするのですが、Objective-Cでもできないかと思い調べてみました。ですが、NSArray自体にはそのようなメソッドが用意されていません。ひょっとして出来ないのかと思っていたら、ちょっと面白い方法で集計関数のようなものが実装されていることがわかりました。

Objective-CにはKey-Value Codingという概念があって、それを使って実装されているようです。Key-Value Codingについては正直全然理解していないのでここでの解説は避けます。すみません。

NSArrayのvalueForKeyPathを使って以下のように問い合わせを行うと、先ほどのPythonの例と同様にNSArray中の最大値を持つオブジェクトを取得することが出来るようです。
// noとnameプロパティを持つPersonクラスがあると仮定して・・・
id akisute = [[Person alloc] initWithNo:1 name:@"akisute"];
id abesi= [[Person alloc] initWithNo:2 name:@"abesi"];
id hidebu= [[Person alloc] initWithNo:3 name:@"hidebu"];
NSArray *array = [NSArray arrayWithObjects:akisute, abesi, hidebu, nil];
NSNumber *maxNo = [array valueForKeyPath:@"@max.no"];
valueForKeyPathの引数に、@max.noというキーを渡すところがキモです。これで、NSArrayのインスタンスに含まれるオブジェクトのnoプロパティの最大値を求めることが出来ます。
同様にして、名前の最大値を求めたいときには、
NSString *maxName = [array valueForKeyPath:@"@max.name"];

とすれば取れます。また、一番長い名前の長さを求めたいときは、以下のように指定することができます。
NSNumber *maxCountOfName = [array valueForKeyPath:@"@max.name.count"];

@max以外にも、@avg, @sum, @count, @min, @unionOfObjects, @distinctUnionOfObjectsなどが用意されているみたいです。

詳しくは荻原さんのObjective-C 2.0本をご参照あれ。いや、この本は本当に買ってよかったです。Objective-Cのバイブルですね。

詳解 Objective-C 2.0
荻原 剛志
4797346809

Windows起動時にhal.dllが見つかりませんと言われた場合の対処法

久しぶりにBootCampでWindows XPを起動しようとしたら、hal.dllが見つかりませんといった具合のエラーメッセージが表示されてうんともすんとも言わなくなってしまいました。dllが破損したのかと思い調べてみたところ、このエラーメッセージはC:¥boot.iniファイルの設定が破損された場合に表示されるメッセージだと言うことが分かりました(さすがマイクロソフト、エラーメッセージの意味不明さには定評があります)。なるほど、確かにboot.iniファイルが消えてました。

修復するにはWindows XPをCDから起動して、回復コンソールを立ち上げます。画面の表示に従えば大丈夫だと思います。
回復コンソールが起動したら、以下のコマンドを入力。
bootcfg /list

これで起動設定(boot.iniファイルの中身)の一覧が表示されます。私の場合は、boot.ini自体が存在しないので、設定がありませんと怒られてしまいました。
設定が破損しているのを確認したら、以下のコマンドで修復を行います。
bootcfg /rebuild

途中、3回ぐらい選択肢が表示されますが、以下のように回答します。
インストールをブート一覧に追加しますか?
YES

ブートIDを入力してください
適当でかまいません。MacでBootCampしている私の場合はWindowsと入力しました。普通のWindowsマシンの場合にはWindows XP Professionalとか入れておくとわかりやすいのではないでしょうか。

オプションを指定してください
/fastdetect

これで完了です。あっさり終了するのでちょっと拍子抜け。あとはコンピューターを再起動すれば問題なくWindows XPが起動すると思います。