wigging
wigging

Reputation: 9170

Dynamically update numbers in a NSTextField

I am having trouble making a dynamic unit converter. I have two text fields: one for "inches" and another for "feet". My goal is to convert the inches to feet as the numbers are being entered and vice versa. I want the action to happen dynamically, not when the "Enter" or "Tab" button is pressed.

I am using the NSTextField with Number Formatter. So I need the formatter to also work as the calculation takes place. I have tried the -(void)controlTextDidChange method but the formatter does not work with it.

See below for my current code which does not work dynamically.

#import <Foundation/Foundation.h>

@interface Convert : NSObject {
    IBOutlet NSTextField *inches;
    IBOutlet NSTextField *feet;
}

- (IBAction)textDidChange:(id)sender;

@end

and

#import "Convert.h"

@implementation Convert

- (IBAction)textDidChange:(id)sender {

    if (sender == inches) {
        float myInches = [inches floatValue];
        [feet setFloatValue: (myInches/12)];
    }
    else if (sender == feet) {
        float myFeet = [feet floatValue];
        [inches setFloatValue: (myFeet*12)];
    }
}

@end

Can anyone tell me how to accomplish this without having to press the "Enter" or "Tab" key?

...Now trying the NSValueTransformer approach using NSForm instead of NSTextField...

#import <Foundation/Foundation.h>

@interface LengthConverter : NSValueTransformer

+ (Class)transformedValueClass;
+ (BOOL)allowsReverseTransformation;

- (id)transformedValue:(id)value;
- (id)reverseTransformedValue:(id)value;

@end

and

#import "LengthConverter.h"

@implementation LengthConverter

+ (Class)transformedValueClass {
    return [NSNumber class];
}

+ (BOOL)allowsReverseTransformation {
    return YES;   
}

- (id)transformedValue:(id)value {
    if (value == nil) return nil;

    if (![value respondsToSelector: @selector(doubleValue)]) {
        [NSException raise:NSInternalInconsistencyException format:@"Value does not respond",[value class]];
    }

    CGFloat inchesInputValue = [value doubleValue]; // handles NSString and NSNumber
    CGFloat feetOutputValue = inchesInputValue / 12;

    return [NSNumber numberWithDouble: feetOutputValue];
}

- (id)reverseTransformedValue:(id)value {
    if (value == nil) return nil;

    if (![value respondsToSelector: @selector(doubleValue)]) {
        [NSException raise:NSInternalInconsistencyException format:@"Value does not respond",[value class]];
    }

    CGFloat feetInputValue = [value doubleValue]; // handles NSString and NSNumber
    CGFloat inchesOutputValue = feetInputValue * 12;

    return [NSNumber numberWithDouble: inchesOutputValue];
}

@end

Upvotes: 1

Views: 959

Answers (1)

justin
justin

Reputation: 104698

You can bind values using NSValueTransformers.

NSValueTransformer offers object to object transformations, which you can also use in XIBs and bindings. The system defines a few, and you can define your own quite easily. Apple's TemperatureConverter sample would be a good starting point for an introduction to NSValueTransformer.

Update in response to sample

In + [ApplicationDelegate initialize], you'll see the program register all the transformers it needs. They register to a name, such as @"centrigradeFromKelvin".

Once the trafos you will need are registered, you can use the transformer in the NIB. In the TemperatureConvertoer example, you can:

  1. navigate to the form cells
  2. select Form Cell - Centigrade
  3. Press cmd+opt+7 to view the fields bindings
  4. The value is bound to a value in SharedDefaults - the value transformer is set as "centrigradeFromKelvin".

Upvotes: 2

Related Questions