6

バインド可能にしたいプロパティを持つ NSView サブクラスがあります。サブクラスに以下を実装しました。

myView.h:

@property (readwrite, retain) NSArray *representedObjects;

myView.m:

@synthesize representedObjects;

+(void)initialize
{
    [self exposeBinding: @"representedObjects"];
}


-(void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
{
    if ([binding isEqualToString:@"representedObjects"]) {
        [observableController addObserver: self forKeyPath:@"arrangedObjects" options:NSKeyValueChangeNewKey context:nil];
    } else {
        [super bind: binding toObject:observableController withKeyPath:keyPath options: options];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"arrangedObjects"]) {
        [self setRepresentedObjects: [object arrangedObjects]];
    }
}

次に、arrayController へのバインディングを次のように作成します-[AppController awakeFromNib]

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

これはバインディングを実装する正しい方法ですか? それには多くの定型コードが含まれているため、何か間違ったことをしていると思います。

NSObject は手動で行ったことを自動的に実装すると思っていまし-bind:toObject:withKeyPath:options:たが、そうではないようです。コメントアウトすると-bind:toObject:withKeyPath:options:、setRepresentedObjects メソッドは呼び出されません。

追加情報:さらに調査を行った結果、元のアプローチは正しく、オーバーライドする必要があるという結論に達しました-bind:toObject:withKeyPath:options:Cocoa Bindings Programming Topics: How Do Bindings Work?からの引用です。:

bind:toObject:withKeyPath:options: メソッドでは、オブジェクトは少なくとも次のことを行う必要があります。

  • 設定されているバインディングを特定する
  • どのキーパスとどのオプションを使用して、どのオブジェクトにバインドされているかを記録します
  • 変更の通知を受け取るために、バインドされているオブジェクトのキーパスのオブザーバーとして登録します

リスト 2 のコード サンプルは、角度バインディングのみを処理するジョイスティックの bind:toObject:withKeyPath:options: メソッドの部分的な実装を示しています。

リスト 2 ジョイスティック クラスの bind:toObject:withKeyPath:options メソッドの部分的な実装:

static void *AngleBindingContext = (void *)@"JoystickAngle";
 
- (void)bind:(NSString *)binding
 toObject:(id)observableObject
 withKeyPath:(NSString *)keyPath
 options:(NSDictionary *)options
{
 // Observe the observableObject for changes -- note, pass binding identifier
 // as the context, so you get that back in observeValueForKeyPath:...
 // This way you can easily determine what needs to be updated.
 
if ([binding isEqualToString:@"angle"])
 {
    [observableObject addObserver:self
                   forKeyPath:keyPath
                  options:0
                  context:AngleBindingContext];
 
    // Register what object and what keypath are
    // associated with this binding
    observedObjectForAngle = [observableObject retain];
    observedKeyPathForAngle = [keyPath copy];
 
    // Record the value transformer, if there is one
    angleValueTransformer = nil;
    NSString *vtName = [options objectForKey:@"NSValueTransformerName"];
    if (vtName != nil)
    {
        angleValueTransformer = [NSValueTransformer
            valueTransformerForName:vtName];
    }
 }
 // Implementation continues...

これは、Joystick クラス (NSView サブクラス) がオーバーライドする必要があることを明確に示しています-bind:toObject:withKeyPath:options:

これは驚くべきことです。これを実行するコード サンプルが他にないため、この結論には懐疑的でした。ただし、Apple の公式ドキュメントにはオーバーライドする必要があると記載-bind:toObject:withKeyPath:options:されているため、これが正しいアプローチであると結論付けています。

誰かが私が間違っていることを証明できれば、とてもうれしいです!

4

3 に答える 3

1

いいえ、オーバーライドする必要はありませんbind:

Peter Hoseyが以前の回答へのコメントで書いたように、exposeBinding:KVCおよびKVO準拠のアクセサーとセッターを呼び出して実装できます。

MyView.h:

@interface MyView : NSView {
    NSArray *_representedObjects;
}

// IBOutlet is not required for bindings, but by adding it you can ALSO use
// an outlet
@property (readonly, retain) IBOutlet NSArray *representedObjects;

@end

MyView.m:

+ (void)initialize {
    [self exposeBinding:@"representedObjects"];
}

// Use a custom setter, because presumably, the view needs to re-draw
- (void)setRepresentedObjects:(NSArray *)representedObjects {
    [self willChangeValueForKey:@"representedObjects"];
    // Based on automatic garbage collection
    _representedObjects = representedObjects;
    [self didChangeValueForKey:@"representedObjects"];

    [self setNeedsDisplayInRect:[self visibleRect]];
}

次に、プログラムでバインディングを設定できます。

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

ただし、Interface Builderでバインディングを設定するには、カスタムパレットを作成する必要があります。

于 2011-09-12T21:27:30.250 に答える
1

いいえ、そのグルー コードは必要ありません。

「そうではないと思われる」とはどういう意味ですか? 省略したらどうなる?

于 2008-12-14T21:06:26.183 に答える
0

-bind:toObject:withKeyPath:options:そのビューでバインディングを実装する場合は、必ずカスタム ビューで実装する必要があります。myView.m での実装はかなり適切です。

于 2009-01-08T03:38:20.170 に答える