独自の Value Transformer を作る

Cocoa には Value Transformer ってのが用意されていて、バインディングの際にそれを使うことで単純に値を引き渡す以上のことができる。

標準では次のものがあるけど、これ以外にも自分で Value Transformer を作ることができる。

NSNegateBooleanTransformerName

NSIsNilTransformerName

NSIsNotNilTransformerName

NSUnarchiveFromDataTransformerName

NSKeyedUnarchiveFromDataTransformerName

その方法を学んだので、ちょっとメモ。

まず、NSValueTransformer のサブクラスを作る。新たにファイルを作ってもいいし、既にあるファイルに加えてもいいはず。

class ArrayExistTransformer < OSX::NSValueTransformer

end

この中で、最低 transformedValueClasstransformedValue(value)allowsReverseTransformation を定義する。

transformedValueClass では変換後に返す値のクラスを指定する。NSString なら NSString.class、NSNumber なら NSNumber.class という感じ。

NSNumber を返す場合は、次のようにする。ちなみにここでは include OSX して OSX:: を省略した形で書いてある。

def transformedValueClass

NSNumber.class

end

次に transformedValue(value) で実際の変換を定義する。value に元の値が入っている。例えば、テーブルに関連するボタンで、テーブルにバインドされた Array Controller が空の場合はボタンをクリックできないようにしたい場合、まずは、Interface Builder のそのボタンの binding で Enabled を選んでその Array Controller にバインドして、Controller Key を arrangedObjects にして Value Transformer に新たに作る Value Transformer の名前を入力する。この場合は、Array Controller の arrangedObject の要素が 0 の時に返り値が 0 で、そうでないときは 1 になればいいので、次のようなスクリプトを書く。ちなみに、返り値は 0、1 なので NSNumber を指定する。

def transformedValue(value)

if value.nil? || value.length == 0

return 0

else

return 1

end

end

あと、ここで定義した変換の逆の変換を定義するかどうかを決める(この例の場合は当てはまらないが)。allowsReverseTransformation で true ならば reverseTransformedValue(value) で逆変換を定義して false ならばしない。

def allowsReverseTransformation

false

end

def reverseTransformedValue(value)

true にした場合、ここに逆変換のスクリプトを書く

end

これで、準備が終わった。この Value Transformer を使うには、アプリケーションのスクリプトで名前を与えて使えるようにしないといけない。AppController というか、アプリケーションのメインのスクリプトファイルに applicationDidFinishLaunching(application) を加えて行う。最初 awakeFromNib に入れてみたけど、使えなかった。

まず、定義したサブクラスのインスタンスを作り、NSValueTransformersetValueTransformer_ForName(transformer,name) で名前を付けて使えるようにする。ここでは、上で定義した Value Transformer に ArrayExistTransformer という名前を付けてみる。

def applicationDidFinishLaunching(application)

transformer = ArrayExistTransformer.new

NSValueTransformer.setValueTransformer_forName(transformer,"ArrayExistTransformer")

end

まあ、2行で書いたけど次のように1行にしてしまうこともできる。見づらくなるので分けて書いた方がいいと思うけど。

NSValueTransformer.setValueTransformer_forName(ArrayExistTransformer.new,"ArrayExistTransformer")

最近わかったんだけど、

transformer = ArrayExistTransformer.init.alloc

は、

transformer = ArrayExistTransformer.new

でも問題なく動く。

最後にスクリプトをまとめてみる。

AppController

def applicationDidFinishLaunching(application)

transformer = ArrayExistTransformer.new

NSValueTransformer.setValueTransformer_forName(transformer,"ArrayExistTransformer")

end

ArrayExistTransformer(別の NSObject サブクラスファイルを作るか、AppController のファイルの最後にでもつけておく)

class ArrayExistTransformer < OSX::NSValueTransformer

include OSX

def transformedValueClass

NSNumber.class

end

def allowsReverseTransformation

false

end


def transformedValue(value)

if value.nil? || value.length == 0

return 0

else

return 1

end

end

end