Object Controller を使ってみる

ここでは、NSObjectController を使ったときに試したことをメモしていく。ようやく理解しかけたか?という段階なので、今のところはメモの羅列にすぎない。後で内容がたまって時間ができたらまとめ直すと思う。

これを使う利点としては、異なるサブクラスファイルからインターフェイス上のオブジェクトに簡単にアクセスできるとかか。オブジェクトを一括管理できるとか、あとはそれらのオブジェクトの値をまとめて User Defaults に保存したりできるとかか。まだ、試し始めたばかりなので、どういう場面で使うのがいいかよくわかっていない。

Object Controller を Interface Builder で設定する

それは置いておいて、普通に使うときは、Interface Builder で Object Controller のインスタンスを Nib ファイルに追加して、そこで設定をする。

まず、Interface Builder の Library で Object Controller を選んで、Nib ファイルに追加する。

追加したら、これで扱いたいオブジェクトを Inspector の Attributes で追加する。ここでは、text1 と text2 という2つにしてみた。あと、アプリケーションが立ち上がったときに、これら2つがすぐ使えるように Prepares Content にチェックを入れておく。

これで、結びつけたい Text Field を Window に追加する。

これをそれぞれ text1、text2 にバインドする。

変更がすぐに反映されてほしいので、Continuously Updates Value にチェックを入れてみた。でも、入れると入れないでどう差があるのかわからない。

次に、App Controller というか、メインのサブクラスのファイルで、outlet に objCtl というのを追加して、Interface Builder 上で結びつける。

ib_outlets :objCtl

これで、スクリプトから Object Controller にアクセスできる。

Object Controller にアクセスする

2つの Text Field にテキストを表示させるには、Object Controller にオブジェクトを追加するか、中身に直接アクセスする。

まずは、オブジェクトを追加する方法。Array Controller は、NSDictionary オブジェクトの配列を NSArray として管理しているけど、Object Controller は、NSDictionary オブジェクトを管理している。ということで、Ruby の Hash を使えばいい。

Object Controller に Hash オブジェクトを追加するには、setContentaddObject を使う。ただ、これだと全くの置き換えになるので、使っていいものかどうかはよくわからない。が、とりあえず、メモとして使い方は、次の通り。

hash = Hash.new

hash["text1"] = "text field 1"

hash["text2"] = "text field 2"

@objCtl.setContent(hash)

これを awakeFromNib にでも入れて実行すると次のようになる。

ただ、もっと簡単に、直接値を書き換える方法がある。content で、Object Controller の中身に NSDictionary としてアクセスできるので、次のように書けば直接値が変更できる。

@objCtl.content["text1"] = "text field 1"

@objCtl.content["text2"] = "text field 2"

これを実行しても、上と同じ結果になる。ただ、上でやったように、Prepares Content にチェックが入っていないとエラーが出る。チェックを入れない場合は、setContent で key もちゃんと指定しないといけないようだ。

p @objCtl.content

として中を確認すると、次のような結果が返ってくる。

#<NSCFDictionary {#<NSCFString "text1">=>#<NSCFString "text field 1">, #<NSCFString "text2">=>#<NSCFString "text field 2">}>

ここまでは、Ruby っぽい書き方で、Objective-C っぽく(RubyCocoa の書式で)書くと、次のようになる。

@objCtl.content.setValue_forKeyPath("text field 1","text1")

@objCtl.content.setValue_forKeyPath("text field 2","text2")

アクセスする場合は、こんな感じ。

@objCtl.content.valueForKeyPath("text1") ## @objCtl.content["text1]

@objCtl.content.valueForKeyPath("text2") ## @objCtl.content["text1]

これと同じように、多分テキスト以外のどんなオブジェクトも簡単にバインドできるはず。

ボタンの Enabled を一括管理する

まあ、上のような場合は、Object Controller を使う意味が薄いんだけど、例えば、3つのボタンを一括して機能しなくしたい場合などには重宝する。

次のような3つのボタンがあるとする。

これを普通にスクリプトで機能しなくするには、それぞれの outlet に対して setEnabled(false) を使えばいい。だけど、それだと3つに対して個別に操作する必要がある。

そこで、Object Controller に buttonSetting という key を追加してみる。

次に、3つのボタンそれぞれに対して Binding の設定で Availability の Enabled で Bind to に Object Controller を選び、Model Key Path に buttonSetting を選ぶ。

最後にスクリプトで、機能させたくないときに 0、機能させたいときに 1 を入れると3つとも設定が変わる。

@objCtl.content["buttonSetting"] = 0

User Defaults Controller にバインドする

とりあえずの最後に、Object Controller の Value を User Defaults に書き込む方法をメモする。Shared User Defaults の Array Controller の所にちょっと書いたけど、まとめて NSData として保存できる。Shared User Defaults についての細かいことは、そのページがあるのでそちらを参照してください。

まず、Object Controller を選んで、Inspector の Bindings で、Content Object を Shared User Defaults Controller にバインドして、Controller Key を設定する。ここでは、textData としておく。そして、Handles Content As Compound Value にチェックを入れる。あと、Value Transformer で、NSUnarchiveFromData を選ぶ。

これで Object Controller の値が保存され、次回アプリケーションが起動したときに読み込まれる。

初期値を設定して、初回起動時に読み込ませたい場合は、この方法だとちょっと面倒。Shared User Defaults にあるようにあらかじめ UserDefaults.plist などのように初期設定のファイルを作り読み込むように設定するんだけど、Data として値を保存する方法だと、初期設定の値も Data 型にする必要がある。この例で言えば、全部が空白の状態で起動させていいのなら、UserDefaults.plist に何も登録しておかないと、Prepares Content にチェックが入れてあれば問題なく立ち上がる。

でも、何か決まった初期値を設定しておきたい場合は、UserDefaults.plist で空の値を Data 型で指定するとエラーが出て起動しない。まあ、エラー処理をすればいいわけなんだけど。この場合の対処法は、とりあえず setContent なんかで値をとりあえず入れて起動させてから、初回に読み込ませたい値にしてアプリケーションを終了する。すると、Preferences フォルダに保存される plist ファイルに Data 型としての値があるので、その値をコピーして、UserDefaults.plist に貼付ける。

ここに入ってる値は次の通り。

これが面倒だと言う場合は、別の方法で。この次の方法は、オブジェクトの数が少ない場合は初期値が簡単に返られるしいいかも。

Shared User Defaults Controller にバインドするときに、Handles Content As Compound Value にはチェックを入れず、Value Transformer は空白にする。こうすると、値は Dictionary として保存される。

なので、UserDefaults.plist の中身も、Dictionary を選んで、それぞれの key に初期値を設定する。設定する数が少ない場合はこれの方が問題が少ない。

あと、初期設定ファイルを使わずに、毎回起動時に値を設定したい場合(リセットするなど)は、awakeFromNib ではなく、applicationDidFinishLaunching(application) に入れないと、そんなのない、と怒られる。