I have an ImageView which shows a lock, informing if an opened file is locked or not. I have 2 images for locked and unlocked cases. I want synchronize the displayed image with boolean value of my object representing an opened file.
To do this I want my ViewController to change the image in my ImageView depending on lock state of object. So both object and ViewController have a property "isLocked".
How can I synchronize them? It is easy in IB but I don't know how to do it programmatically. I tried in initialize method of my ViewController to use:
[ViewController bind:@"value" toObject:[ArrayController selection] withKeyPath:@"isLocked" options:nil];
But it doesn't work. In documentation it is said that I have to expose my binding before using it.
I try to put the following code in initializer method of my object:
[self exposeBinding:@"isLocked"];
But Xcode doesn't recognize this method.
Does somebody have experience with this kind of bindings establishing?
As @nick says, you want Key-Value-Observing.
[arrayController addObserver:self
forKeyPath:@"selection.isLocked"
options:NSKeyValueObservingOptionNew
context:@"this_context"]
Then when isLocked changes the -observeValueForKeyPath:ofObject:change:context: method that you have added to your viewController will be called (as long as you only manipulate isLocked in a KVC compliant way).
The options parameter lets you optionally tweak exactly what conditions will trigger the notification and what data is sent along with the notification. The context parameter is there to help you distinguish between notifications that you registered to receive and notifications your superclass registered to receive. It is optional.
Bindings seem like they might be useful to keep two values in sync. However, this is not what they do at all.
Yes, lots of things seem to give the impression that this is what they do, and there isn't much saying that this isn't what they do, also lots of people believe that this is what they do - but no, you cannot use them for this.
Only a handful of classes support bindings (they are listed here) and then, and this is the important bit, those classes only support binding their named bindings, and these bindings are not instance variables. eg NSTextField has a 'fontFamilyName' binding yet NSTextField does not have a 'fontFamilyName' property or instance variable, even a derived one. NSTextField does have a 'isBordered' property but not a binding - so you cannot bind 'isBordered'.
It does not mean anything to 'bind' an arbitrary property of an arbitrary Class.
Yes, you can bind two arbitrary values, the following code works just fine:
#import <Cocoa/Cocoa.h>
@interface SomeObject : NSObject
@property (retain,nonatomic) id someValue;
@end
@implementation SomeObject
@end
int main()
{
SomeObject *source=[SomeObject new];
SomeObject *target=[SomeObject new];
[target bind:@"someValue" toObject:source withKeyPath:@"someValue" options:0];
[source bind:@"someValue" toObject:target withKeyPath:@"someValue" options:0];
[source setSomeValue:@(42)];
NSLog(@"target: %@",[target someValue]);
[target setSomeValue:@(22)];
NSLog(@"source: %@",[source someValue]);
return 0;
}
As far as I can tell, the problem is the bit [ArrayController selection]
. The first problem is that ArrayController
is (or should be) a class, and getting the class's selection is probably pointless. The other problem is that even if this were an instance, you would be binding to the selection at the time of the call, which is almost certainly not what you want. You want to track the current selection as it changes.
So what you want is probably something like the following:
[myViewController bind:@"value" toObject:myArrayController withKeyPath:@"selection.isLocked" options:nil];