是否有必要在 NSView 子类中重写 bind:toObject:withKeyPath:options: 来实现绑定?
我有一个 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]];
}
}
然后我在 -[AppController awakeFromNib]
中创建到 arrayController 的绑定:
[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];
这是实现绑定的正确方法吗? 它涉及很多样板代码,这让我觉得我做错了什么。
我认为 NSObject 会自动实现我在 -bind:toObject:withKeyPath:options:
中手动完成的操作,但事实似乎并非如此。 如果我注释掉 -bind:toObject:withKeyPath:options:
,则永远不会调用 setRepresentedObjects 方法。
附加信息: 我做了更多的调查,并得出结论,我原来的方法是正确的,你必须重写 -bind:toObject:withKeyPath:options:
。 这是 的引用Cocoa 绑定编程主题:绑定如何工作?:
在其 bind:toObject:withKeyPath:options: 方法中,对象至少必须执行以下操作:
- 确定正在设置哪个绑定
- 记录它使用什么键路径和什么选项绑定到什么对象
- 注册为所绑定对象的键路径的观察者,以便接收更改通知
清单 2 中的代码示例显示了 Joystick 的 bind:toObject:withKeyPath:options: 方法的部分实现,仅处理角度绑定。
清单 2 Joystick 类的 bind:toObject:withKeyPath:options 方法的部分实现:
<块引用>static void *AngleBindingContext = (void *)@"JoystickAngle"; - (void)绑定:(NSString *)绑定 toObject:(id)observableObject withKeyPath:(NSString *)keyPath 选项:(NSDictionary *)选项 { // 观察 observableObject 的变化 -- 注意,传递绑定标识符 // 作为上下文,所以你可以在observeValueForKeyPath:...中得到它 // 这样您就可以轻松确定需要更新的内容。 if ([绑定 isEqualToString:@"angle"]) { [observableObject addObserver:self forKeyPath:keyPath 选项:0 上下文:AngleBindingContext]; // 注册什么对象和什么keypath // 与此绑定关联 ObservableObjectForAngle = [observableObject 保留]; 观察到的KeyPathForAngle = [keyPath副本]; // 记录值转换器,如果有的话 角度值转换器 = nil; NSString *vtName = [选项 objectForKey:@"NSValueTransformerName"]; if (vtName!= nil) { 角度值转换器 = [NSValueTransformer valueTransformerForName:vtName]; } } // 执行继续...
这清楚地表明 Joystick 类(它是 NSView 子类)需要重写 -bind:toObject:withKeyPath:options:
。
我觉得这很令人惊讶。 我对这个结论表示怀疑,因为我没有发现其他代码示例可以做到这一点。 然而,正如苹果官方文档所说,我应该覆盖 -bind:toObject:withKeyPath:options:
我得出结论,这是正确的方法。
如果有人能证明我错了,我会很高兴!
I have an NSView subclass which has property which I want to be bindable. I've implemented the following in the subclass:
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]];
}
}
I then create the binding to an arrayController in -[AppController awakeFromNib]
:
[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];
Is this the correct way of implementing binding? It involves a lot of boiler plate code which makes me think that I'm doing something wrong.
I thought that NSObject would automagically implement what I have done manually in -bind:toObject:withKeyPath:options:
but this doesn't seem to be the case. If I comment out my -bind:toObject:withKeyPath:options:
the setRepresentedObjects method is never called.
Additional info:
I've done some more investigating and have reached the conclusion that my original approach is correct and you do have to over ride -bind:toObject:withKeyPath:options:
. Here's a quote from Cocoa Bindings Programming Topics: How Do Bindings Work?:
In its bind:toObject:withKeyPath:options: method an object must as a minimum do the following:
- Determine which binding is being set
- Record what object it is being bound to using what keypath and with what options
- Register as an observer of the keypath of the object to which it is bound so that it receives notification of changes
The code sample in Listing 2 shows a partial implementation of Joystick’s bind:toObject:withKeyPath:options: method dealing with just the angle binding.
Listing 2 Partial implementation of the bind:toObject:withKeyPath:options method for the Joystick class:
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...
This clearly shows that the Joystick class (which is an NSView subclass) needs to override -bind:toObject:withKeyPath:options:
.
I find this surprising. I was skeptical of this conclusion as I have found no other code samples that do this. However, as the offical Apple documentation says I should over ride -bind:toObject:withKeyPath:options:
I conclude that it is the correct approach.
I would be very happy if someone could prove me wrong!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不,您不需要那个粘合代码。
你所说的“似乎并非如此”是什么意思? 如果忽略它会发生什么?
No, you shouldn’t need that glue code.
What do you mean by “doesn’t seem to be the case”? What happens if you omit it?
不,没有必要重写
bind:
。正如 Peter Hosey 在之前答案的评论中所写,您可以调用
exposeBinding:
并实现 KVC 和 KVO 兼容的访问器和设置器。MyView.h:
MyView.m:
然后您可以通过编程方式设置绑定:
但是,要在 Interface Builder 中设置绑定,您必须创建一个自定义调色板。
No, it is not necessary to override
bind:
.As Peter Hosey wrote in the comment to the earlier answer, you can call
exposeBinding:
and implement KVC- and KVO-compliant accessors and setters.MyView.h:
MyView.m:
Then you can set the binding programmatically:
To set the binding in Interface Builder, however, you must create a custom palette.
如果您想在自定义视图中实现绑定,那么您肯定需要在该视图中实现 -bind:toObject:withKeyPath:options: 。 您在 myView.m 中的实现非常正确。
You definitely DO need to implement
-bind:toObject:withKeyPath:options:
in a custom view if you want to implement bindings in that view. Your implementation in myView.m is pretty much spot on.