I have observers added to several NSTextFields to monitor changes in each text field. The key of each text field is configured in Interface Builder at Bindings -> Value -> Model Key Path
. When the number in one text field is changed, the other text fields automatically update their value. Since an observer was added to each text field, I must remove the other observers to avoid a loop that will crash the app. After the observers are removed, I must add them back to the other text fields so they can be monitored for input by the user. My approach is working fine, but I can see how this can be cumbersome if a lot of observers are added.
Is there a way to streamline this to where I don't have to add and remove observers depending on the user's input?
#import "Converter.h"
@interface Converter ()
@property double kilometer, mile, foot;
@end
@implementation Converter
- (void)awakeFromNib {
[self addObserver:self forKeyPath:@"kilometer" options:0 context:nil];
[self addObserver:self forKeyPath:@"mile" options:0 context:nil];
[self addObserver:self forKeyPath:@"foot" options:0 context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"kilometer"]) {
[self removeObserver:self forKeyPath:@"mile"];
[self removeObserver:self forKeyPath:@"foot"];
NSLog(@"kilometers");
[self setMile: [self kilometer] * 0.62137119 ];
[self setFoot: [self kilometer] * 3280.8399 ];
[self addObserver:self forKeyPath:@"mile" options:0 context:nil];
[self addObserver:self forKeyPath:@"foot" options:0 context:nil];
}
if ([keyPath isEqualToString:@"mile"]) {
[self removeObserver:self forKeyPath:@"kilometer"];
[self removeObserver:self forKeyPath:@"foot"];
NSLog(@"miles");
[self setKilometer: [self mile] * 1.609344 ];
[self setFoot: [self mile] * 5280 ];
[self addObserver:self forKeyPath:@"kilometer" options:0 context:nil];
[self addObserver:self forKeyPath:@"foot" options:0 context:nil];
}
if ([keyPath isEqualToString:@"foot"]) {
[self removeObserver:self forKeyPath:@"kilometer"];
[self removeObserver:self forKeyPath:@"mile"];
NSLog(@"feet");
[self setKilometer: [self foot] * 0.0003048 ];
[self setMile: [self foot] * 0.00018939394 ];
[self addObserver:self forKeyPath:@"kilometer" options:0 context:nil];
[self addObserver:self forKeyPath:@"mile" options:0 context:nil];
}
}
@end
Here's a screen shot of the user interface:
To help clarify what the code is doing (or suppose to be doing):
User wants to convert feet to kilometer and miles, so he inputs a value into the feet text field. The appropriate conversion factors are used.
The user wants to convert kilometers to miles and feet, so he inputs a value into the kilometer field. A different set of conversion factors are used.
etc...
By customizing your setter method and implementing + (BOOL)automaticallyNotifiesObserversForKey:
, you can manually update notifications for these properties in a nested way.
The following codes are tested to be working. (Note that I did not use your coefficients and property names).
#define BEGIN_UPDATE [self willChangeValueForKey:@"m"];\
[self willChangeValueForKey:@"km"];\
[self willChangeValueForKey:@"f"];
#define END_UPDATE [self didChangeValueForKey:@"f"];\
[self didChangeValueForKey:@"km"];\
[self didChangeValueForKey:@"m"];
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"f"]||[key isEqualToString:@"km"]||[key isEqualToString:@"m"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (void)setF:(float)f {
BEGIN_UPDATE
_m = 0.5*f;
_km = 0.1*f;
_f = f;
END_UPDATE
}
- (void)setKm:(float)km {
BEGIN_UPDATE
_km = km;
_f = 10*km;
_m = 5*km;
END_UPDATE
}
- (void)setM:(float)m {
BEGIN_UPDATE
_m = m;
_km = 0.2*m;
_f = 2*m;
END_UPDATE
}