Re: NSEditor, NSEditorRegistration Protocols
Re: NSEditor, NSEditorRegistration Protocols
- Subject: Re: NSEditor, NSEditorRegistration Protocols
- From: mmalcolm crawford <email@hidden>
- Date: Sun, 7 Mar 2004 11:17:12 -0800
On Mar 7, 2004, at 6:27 AM, Allan Odgaard wrote:
On 6. Mar 2004, at 21:28, mmalcolm crawford wrote:
[...] Simply expose your key and make all changes to that key either
through set<Key>: or setValue:forKey:, these methods will send a
will/didChangeValueForKey:, which the controller should observe
[...]
This is wrong.
So how should it be made then?
If I write a slider class like this:
@implementation MySlider : NSView
- (void)setValue:(int)aValue
- (void)mouseDragged:(NSEvent*)theEvent
@end
And a model class like this:
@interface MyModel : NSObject
{
int maxConnections;
}
@end
Then I can use the following to propagate changes from either layer to
the other:
[slider bind:@"value" toObject:model withKeyPath:@"maxConnections"
options:nil];
[model bind:@"maxConnections" toObject:slider withKeyPath:@"value"
options:nil];
What are the disadvantages with this approach?
Redundancy. A single binding makes the inter-dependency clear, and the
current implementation makes the information transfer mechanism and
policy clear.
There's a to-one relationship from a view binding to a controller, and
a potential to-many from controller to view. It makes sense for a
change to a view binding value to be communicated directly to the
corresponding controller, and for a change to a controller value to be
broadcast to anyone who expressed interest.
I realize that the above is not what IB or AppKit views do because a)
in IB you can only bind to controllers (but is that a design
limitation or just IB being "smart"? since there actually is the
ability to bind to File's Owner, which is not necessarily an
NSController subclass)
I think it's been stated before that the constraints on what bindings
can be established in IB may be "artificial", and may be relaxed in the
future.
and b) the "exposed keys" does not exist as actual keys for the view
objects.
So does all AppKit view classes know of NSControllers?
No, they know how to observe...
The controller observes the model, not the view. The view in turn
observes the controller. Value changes are propagated from view, via
controller, to model using KVC.
The observing between controller and view would have to go both ways,
wouldn't it? I mean, if only the view observes the controller, changes
made in the GUI would not be propagated to the controller -- unless
the view has special knowledge of the controller which it is
observing.
The view does not have "special" knowledge of the controller, except
insofar as is specified by the binding...
OTOH the AppKit views are weird -- if I manually want to bind to an
NSSlider, I need to write:
[mySlider bind:@"value" toObject:self withKeyPath:@"sliderValue"
options:nil];
[...]
But it is still unclear how the view would know about the controller?
By the message [mySlider bind:@"value" toObject:self
withKeyPath:@"sliderValue" options:nil], the slider is instructed:
bind:@"value"
If whatever is associated with "value" changes,
toObject:self
tell the specified object that
withKeyPath:@"sliderValue"
its (the object's) "sliderValue" has changed
The slider can obey this instruction by keeping a record of the object
and keypath associated with the binding, and if "value" changes
messaging the object directly:
[object setValue: appropriateValueFor"value" forKeyPath:
@"sliderValue"];
By the message [mySlider bind:@"value" toObject:self
withKeyPath:@"sliderValue" options:nil], the slider is also instructed:
toObject:self
If the specified object's
withKeyPath:@"sliderValue"
"sliderValue" changes
bind:@"value"
update whatever is associated with "value"
options:nil
with no options
The slider can obey this instruction by adding itself as an observer
for the object's keypath ("sliderValue").
One possible(*) implementation that follows this conceptual model:
To keep things as straightforward as possible, the view has two
dictionaries, controllerBindings and keyPathBindings. The keys in the
dictionaries are the binding names, the values the corresponding
controller and keypath respectively.
- (void)bind:(NSString *)binding
toObject:(id)observableController
withKeyPath:(NSString *)keyPath
options:(NSDictionary *)options
{
// observe the controller for changes -- note, pass binding
// as the context, so we get that back in observeValueForKeyPath:...
// that way we can easily determine what needs to be updated.
[observableController addObserver:self
forKeyPath:keyPath
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:binding];
// register what controller and what keypath are
// associated with this binding
[controllerBindings setValue:observableController forKey:binding];
[keyPathBindings setValue:keyPath forKey:binding];
}
If a value associated with a binding is changed, then the corresponding
controller must be informed:
- (void)setAngle:(float)newAngle
{
// inform the controller.
if (angle != newAngle)
{
angle = newAngle;
id controller = [controllerBindings valueForKey:@"angle"];
if (controller != nil)
{
[controller setValue: [NSNumber numberWithFloat:angle]
forKeyPath: [keyPathBindings valueForKey:@"angle"]];
}
}
}
Conversely, if the controller informs the view that a value has
changed, then the view should update the corresponding variable.
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
// we passed the binding as the context when we added ourselves
// as an observer -- use that to decide what to update...
// should ask the change dictionary for the value...
[self setValue:[object valueForKeyPath:keyPath]
forKey:(NSString *)context];
[self setNeedsDisplay:YES];
}
(*) Note: This is one possible implementation that works for me. The
main goal is to explain the modus operandi. This is not a design
recommendation. YMMV.
mmalc
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.