为什么我的 Cocoa 绑定坏了?

问题描述 投票:0回答:6

我有一个带有 NSTextField 的窗口(在 Snow Leopard 中),我已将其绑定到我的 WindowController 类中的 NSString 函数。这个字符串将结合我的数组控制器提供的关于我的表视图的选择和计数的信息。它有一个初始值,

"0 0"
,但在选择或计数发生变化时永远不会更新。绑定看起来像这样(文件的所有者是 MyWindowController):

alt text

我实现了

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

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

我确定我缺少某些东西,但我不知道是什么。有人有什么想法吗?我已经通读了 Apple 的键值观察文档,这似乎是所有必要的。我检查过,我的

keyPathsForValuesAffectingMyString
被调用了,但是
myString
只被调用了一次。我在下面提炼了我的代码(更新 x3)。

更新 1/21

我还在努力解决这个问题。当我

addObserver
self
对于 arrayController 键路径时,通知会按预期触发,所以我的键路径和键值观察机制很好。当我在我的
[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
objective-c cocoa macos cocoa-bindings key-value-observing
6个回答
2
投票

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

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

我不能说这个解释是否正确,但我肯定不能让 +keyPathsForValues… 与 NSArrayControllers 一起工作。


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

0
投票

确保

arrayController
插座已在 Interface Builder 中连接。我猜它是零。


0
投票

不要使用@count 关键字。数组控制器上的绑定和 KVO 将在内容更改时更新。如果那不起作用,那么其他地方就有问题了。

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


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
值,它就有效。

警告:never自己调用

-[setWorkaround:]
——这将有效地冲洗绑定的另一侧(
arrayController.selectedObjects
在这种情况下)。

此方法有一些好处:您可以避免集中式 observeValueForKeyPath:... 并且您的逻辑在正确的位置。而且它的扩展性很好,只需为类似情况添加解决方法 2、3 等等。


0
投票

(目前在 2023 年)我们仍然不能将 NSArrayController 与 keyPathsForValuesAffecting 一起使用。 不,不仅是NSArrayController,还有所有继承NSController的类。

也许我找到了解决办法。

@interface AffectableArrayController : NSArrayController
@end

@implementation AffectableArrayController
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    [super addObserver:observer forKeyPath:keyPath options:options context:([observer isKindOfClass:NSClassFromString(@"NSKeyValueObservance")]) ? (nil) : (context)];
    return;
}

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context
{
    [super removeObserver:observer forKeyPath:keyPath context:([observer isKindOfClass:NSClassFromString(@"NSKeyValueObservance")]) ? (nil) : (context)];
    return;
}
@end

尝试使用上面的类而不是 NSArrayController。 您会收到控制器属性更改的通知。

- (nullable NSString*)derivedValueFromAC
{
     return [self.ac_.selectedObjects.firstObject stringValue];
}

+ (NSSet<NSString*>*)keyPathsForValuesAffectingDerivedValueFromAC
{
     return [NSSet setWithObjects:@"ac_.selectedObjects", nil];
}
© www.soinside.com 2019 - 2024. All rights reserved.