1

各テキストフィールドの変更を監視するために、いくつかのNSTextFieldにオブザーバーを追加しました。各テキストフィールドのキーは、InterfaceBuilderので設定されBindings -> Value -> Model Key Pathます。1つのテキストフィールドの数値が変更されると、他のテキストフィールドの値が自動的に更新されます。各テキストフィールドにオブザーバーが追加されたため、アプリをクラッシュさせるループを回避するために、他のオブザーバーを削除する必要があります。オブザーバーを削除したら、他のテキストフィールドに追加して、ユーザーによる入力を監視できるようにする必要があります。私のアプローチはうまく機能していますが、多くのオブザーバーが追加された場合、これがどのように面倒になるかがわかります。

ユーザーの入力に応じてオブザーバーを追加および削除する必要がない場所にこれを合理化する方法はありますか?

#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

ユーザーインターフェイスのスクリーンショットは次のとおりです。 ユニットコンバータのスクリーンショット

コードが何をしているのか(または何をしていると思われるのか)を明確にするために:

ユーザーはフィートをキロメートルとマイルに変換したいので、フィートのテキストフィールドに値を入力します。適切な変換係数が使用されます。

ユーザーはキロメートルをマイルとフィートに変換したいので、キロメートルフィールドに値を入力します。異なる変換係数のセットが使用されます。

等...

4

2 に答える 2

2

セッターメソッドをカスタマイズして実装することにより+ (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

Assuming that, as I suggested before, you have set your various NSTextFields to be bound continuously to the various properties (kilometer, mile, and foot), one approach is to use a single backing property, let's say foot, and have all other properties affect this one:

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;
}

You'll also need to implement -setNilValueForKey: in case the user clears out the textfields:

- (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];
    }
}

Note: There's no reason to use KVO on self in this context.


For greater precision, multiple backing properties can be used:

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

Whenever any of the bound properties (kilometers, miles, or feet) are set, all of these will be set. Each bound property will have its primitive version as the key path which affects its value. The bound property accessors themselves will simply return their primitive counterparts.

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 に答える