独自の Value Transformer を作る
Cocoa には Value Transformer ってのが用意されていて、バインディングの際にそれを使うことで単純に値を引き渡す以上のことができる。
標準では次のものがあるけど、これ以外にも自分で Value Transformer を作ることができる。
NSNegateBooleanTransformerName
NSIsNilTransformerName
NSIsNotNilTransformerName
NSUnarchiveFromDataTransformerName
NSKeyedUnarchiveFromDataTransformerName
その方法を学んだので、ちょっとメモ。
まず、NSValueTransformer のサブクラスを作る。新たにファイルを作ってもいいし、既にあるファイルに加えてもいいはず。
class ArrayExistTransformer < OSX::NSValueTransformer
end
この中で、最低 transformedValueClass、transformedValue(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 に入れてみたけど、使えなかった。
まず、定義したサブクラスのインスタンスを作り、NSValueTransformer の setValueTransformer_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