为什么我的Cocoa绑定会被破坏?

时间:2011-01-15 11:54:00

标签: objective-c cocoa macos cocoa-bindings key-value-observing

我有一个带有NSTextField的窗口(在Snow Leopard中),我已将其绑定到WindowController类中的NSString函数。该字符串将组合由我的阵列控制器提供的有关我的表视图的选择和计数的信息。它获得初始值"0 0",但在选择或计数更改时不会更新。绑定看起来像这样(File的Owner是MyWindowController):

alt text

我实现了+ (NSSet *)keyPathsForValuesAffecting<key>(下面),但绑定永远不会更新,即使数组控制器的总计数和选择发生了变化。

执行其他故障排除)我最初使用的是NSTextField的显示模式值绑定,但我需要比提供的绑定更复杂的逻辑。然后我开始收听TableView的选择更改/更改事件,它显示数组控制器的内容并动态更改显示模式值绑定,但这感觉就像一个黑客,而且过于复杂。

我确信我有些遗漏,但我不知道是什么。有没有人有任何想法?我已经阅读了Apple的键值观察文档,这似乎是必要的。我已经检查过,我的keyPathsForValuesAffectingMyString被调用,但myString只被调用一次。我在下面提炼了我的代码(更新了x3 )。

更新1/21

我还在试图解决这个问题。对于arrayController键路径,当addObserverself时,通知会按预期触发,因此我的键路径和键值观察机制很好。当我在[self didChangeValueForKey:@"myString"];方法中为相同的密钥调用observeValueForKeyPath时,绑定仍然不会更新,这让我相信它是绑定问题而不是KVO问题。我将更多地阅读绑定机制...

@interface MyWindowController : NSWindowController {
    IBOutlet NSArrayController *arrayController;
}

- (NSArrayController *)arrayController;
- (NSString *)myString;

@end

@implementation MyWindowController

+ (NSSet *)keyPathsForValuesAffectingMyString {
    return [NSSet setWithObjects:
            @"arrayController.arrangedObjects",
            @"arrayController.selection",
            nil];
}

- (NSArrayController *)arrayController {
    return arrayController;
}

- (NSString *)myString {
    // Just as an example; I have more complicated logic going on in my real code
    return [NSString stringWithFormat:@"%@, %@",
            [arrayController valueForKeyPath:@"arrangedObjects.@count"], 
            [arrayController valueForKeyPath:@"selection.@count"]];
}

@end

5 个答案:

答案 0 :(得分:2)

我已经验证了这个完全相同的错误。有关Cocoabuilder的人猜测了为什么会发生错误:

http://www.cocoabuilder.com/archive/cocoa/284396-why-doesn-nsarraycontroller-selection-et-al-fire-keypathsforvaluesaffectingkey.html#284400

我不能说这个解释是否属实,但我肯定无法获得+ keyPathsForValues ...来使用NSArrayControllers。

答案 1 :(得分:1)

我有一个解决方法,但我对它不满意,因为它不应该是必要的,我仍然希望让绑定正常工作。我不接受这个答案,如果有人发布实际修复程序,我会将其删除。 </disclaimer>

@interface MyWindowController : NSWindowController {
    IBOutlet NSArrayController *arrayController;
    IBOutlet NSTextField *fieldThatShouldBeBinded;
}

- (NSString *)myString;

@end

@implementation MyWindowController

- (void)awakeFromNib {
    [arrayController addObserver:self
                      forKeyPath:@"selection"
                         options:0
                         context:NULL];
    [arrayController addObserver:self
                      forKeyPath:@"arrangedObjects"
                         options:0
                         context:NULL];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if( object == arrayController )
        [fieldThatShouldBeBinded setStringValue:[self myString]];
}

- (NSString *)myString {
    return [NSString stringWithFormat:@"%@, %@",
            [arrayController valueForKeyPath:@"arrangedObjects.@count"], 
            [arrayController valueForKeyPath:@"selection.@count"]];
}

@end

答案 2 :(得分:0)

确保在Interface Builder中连接了arrayController插座。我猜这是零。

答案 3 :(得分:0)

请勿使用@count关键字。当内容更改时,阵列控制器上的绑定和KVO将更新。如果这不起作用,那么其他地方就会出现问题。

另一种选择是使用显示模式绑定而不是复合属性。将显示模式Value1绑定到arrayController.arrangedObjects。@ count并将Display Pattern Value2绑定到arrayController.selection。@ count,并将模式设置为“%{value1} @,%{value2} @”

答案 4 :(得分:0)

我遇到了同样的问题并找到了另一种方法(但它仍然是解决方法)。 您必须声明动态变通方法属性。在实现部分中,只返回新的空对象。现在,您可以KVO这个变通方法属性。

@property(nonatomic,retain) NSArray *workaround;
@dynamic workaround;
- (NSArray *)workaround { return [NSArray array]; } // new *every* time
- (void)setWorkaround:(NSArray *)unused { }

+ (NSSet *)keyPathsForValuesAffectingMyString { return [NSSet setWithObject:@"workaround"]; }

要完成这项工作,您仍需要手动将self.workaround绑定到arrayController.selectedObjects(或其他):

- (void)awakeFromNib // or similar place
{
    [super awakeFromNib];
    [self bind:@"workaround" toObject:arrayController withKeyPath:@"selectedObjects" options:nil];
}

手动绑定按预期工作,解决方法使用您绑定的内容进行更新。但是KVO会测试属性值是否真的发生了变化(如果相同则停止传播)。如果您每次都返回新的self.workaround值,则可以正常工作。

警告:从不自己调用-[setWorkaround:] - 这将有效地刷新绑定的另一面(在这种情况下为arrayController.selectedObjects)。

这种方法有一些好处:你可以避免集中的observeValueForKeyPath:...并且你的逻辑位于正确的位置。并且它可以很好地扩展,只需为类似的情况添加解决方法2,3等等。