1

我在几个 NSTextFields 中添加了观察者来监控每个文本字段的变化。每个文本字段的键在 Interface Builder 中配置Bindings -> Value -> Model Key Path。当一个文本字段中的数字更改时,其他文本字段会自动更新其值。由于每个文本字段都添加了一个观察者,因此我必须删除其他观察者以避免导致应用程序崩溃的循环。删除观察者后,我必须将它们添加回其他文本字段,以便可以监视它们以供用户输入。我的方法运行良好,但我可以看到如果添加了很多观察者,这会变得很麻烦。

有没有办法将其简化到我不必根据用户的输入添加和删除观察者的地方?

#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

这是用户界面的屏幕截图: 单位转换器屏幕截图

为了帮助澄清代码正在做什么(或假设正在做什么):

用户想要将英尺转换为公里和英里,因此他在英尺文本字段中输入了一个值。使用适当的转换因子。

用户想要将公里转换为英里和英尺,因此他在公里字段中输入了一个值。使用了一组不同的转换因子。

ETC...

4

2 回答 2

2

通过自定义 setter 方法并实现+ (BOOL)automaticallyNotifiesObserversForKey:,您可以以嵌套方式手动更新这些属性的通知。

以下代码经过测试可以正常工作。(请注意,我没有使用您的系数和属性名称)。

#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
}
于 2013-01-23T17:34:23.210 回答
2

假设,正如我之前建议的那样,您已将各种属性设置为连续NSTextFields绑定到各种属性(公里、英里和英尺),一种方法是使用单个支持属性,比如英尺,并让所有其他属性影响这个:

NSString * const KeyFeet = @"feet";
NSString * const KeyKilometers = @"kilometers";
NSString * const KeyMiles = @"miles";

+ (NSSet *)keyPathsForValuesAffectingKilometers
{
    return [NSSet setWithObject:KeyFeet];
}

static const double kilometerToFeet = 3280.84;
- (void)setKilometers:(double)kilometers
{
    self.feet = kilometerToFeet * kilometers;
}

static const double feetToKilometers = 0.0003048;
- (double)kilometers
{
    return feetToKilometers * self.feet;
}

+ (NSSet *)keyPathsForValuesAffectingMiles
{
    return [NSSet setWithObject:KeyFeet];
}

static const double milesToFeet = 5280;
- (void)setMiles:(double)miles
{
    self.feet = milesToFeet * miles;
}

static const double feetToMiles = 0.000189394;
- (double)miles
{
    return feetToMiles * self.feet;
}

-setNilValueForKey:如果用户清除文本字段,您还需要实施:

- (void)setNilValueForKey:(NSString *)key
{
    if ([key isEqualToString:KeyFeet]) {
        self.feet = 0.0;
    } else if ([key isEqualToString:KeyKilometers]) {
        self.kilometers = 0.0;
    } else if ([key isEqualToString:KeyMiles]) {
        self.miles = 0.0;
    } else {
        [super setNilValueForKey:key];
    }
}

注意self:在这种情况下没有理由使用 KVO 。


为了获得更高的精度,可以使用多个支持属性:

@property (nonatomic) double primitiveKilometers;
@property (nonatomic) double primitiveMiles;
@property (nonatomic) double primitiveFeet;

每当设置任何绑定属性(kilometersmilesfeet)时,都会设置所有这些属性。每个绑定属性都有其原始版本作为影响其值的关键路径。绑定的属性访问器本身将简单地返回它们的原始对应物。

NSString * const KeyPrimitiveFeet = @"primitiveFeet";
NSString * const KeyPrimitiveKilometers = @"primitiveKilometers";
NSString * const KeyPrimitiveMiles = @"primitiveMiles";

+ (NSSet *)keyPathsForValuesAffectingFeet
{
    return [NSSet setWithObject:KeyPrimitiveFeet];
}
static const double feetToMiles = 0.000189394;
static const double feetToKilometers = 0.0003048;
- (void)setFeet:(double)feet
{
    _primitiveFeet = feet;
    self.primitiveKilometers = feetToKilometers * feet;
    self.primitiveMiles = feetToMiles * feet;
}
- (double)feet
{
    return self.primitiveFeet;
}

+ (NSSet *)keyPathsForValuesAffectingKilometers
{
    return [NSSet setWithObject:KeyPrimitiveKilometers];
}
static const double kilometersToFeet = 3280.84;
static const double kilometersToMiles = 0.621371;
- (void)setKilometers:(double)kilometers
{
    _primitiveKilometers = kilometers;
    self.primitiveFeet = kilometersToFeet * kilometers;
    self.primitiveMiles = kilometersToMiles * kilometers;
}

- (double)kilometers
{
    return self.primitiveKilometers;
}

+ (NSSet *)keyPathsForValuesAffectingMiles
{
    return [NSSet setWithObject:KeyPrimitiveMiles];
}
static const double milesToFeet = 5280;
static const double milesToKilometers = 1.60934;
- (void)setMiles:(double)miles
{
    _primitiveMiles = miles;
    self.primitiveFeet = milesToFeet * miles;
    self.primitiveKilometers = milesToKilometers * miles;
}
- (double)miles
{
    return self.primitiveMiles;
}
于 2013-01-23T18:58:02.293 回答