• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: NSEditor, NSEditorRegistration Protocols
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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.


  • Follow-Ups:
    • Re: NSEditor, NSEditorRegistration Protocols
      • From: Allan Odgaard <email@hidden>
References: 
 >NSEditor, NSEditorRegistration Protocols (From: Alan Donsky <email@hidden>)
 >Re: NSEditor, NSEditorRegistration Protocols (From: Allan Odgaard <email@hidden>)
 >Re: NSEditor, NSEditorRegistration Protocols (From: mmalcolm crawford <email@hidden>)
 >Re: NSEditor, NSEditorRegistration Protocols (From: Allan Odgaard <email@hidden>)

  • Prev by Date: Parameter strings in URLs
  • Next by Date: Re: NSEditor, NSEditorRegistration Protocols
  • Previous by thread: Re: NSEditor, NSEditorRegistration Protocols
  • Next by thread: Re: NSEditor, NSEditorRegistration Protocols
  • Index(es):
    • Date
    • Thread