objective-ccocoauser-interfacenstextfield

How to limit NSTextField text length and keep it always upper case?


Need to have an NSTextField with a text limit of 4 characters maximum and show always in upper case but can't figure out a good way of achieving that. I've tried to do it through a binding with a validation method but the validation only gets called when the control loses first responder and that's no good.

Temporarly I made it work by observing the notification NSControlTextDidChangeNotification on the text field and having it call the method:

- (void)textDidChange:(NSNotification*)notification {
  NSTextField* textField = [notification object];
  NSString* value = [textField stringValue];
  if ([value length] > 4) {
    [textField setStringValue:[[value uppercaseString] substringWithRange:NSMakeRange(0, 4)]];
  } else {
    [textField setStringValue:[value uppercaseString]];
  }
}

But this surely isn't the best way of doing it. Any better suggestion?


Solution

  • I did as Graham Lee suggested and it works fine, here's the custom formatter code:

    UPDATED: Added fix reported by Dave Gallagher. Thanks!

    @interface CustomTextFieldFormatter : NSFormatter {
      int maxLength;
    }
    - (void)setMaximumLength:(int)len;
    - (int)maximumLength;
    
    @end
    
    @implementation CustomTextFieldFormatter
    
    - (id)init {
    
       if(self = [super init]){
    
          maxLength = INT_MAX;
       }
    
      return self;
    }
    
    - (void)setMaximumLength:(int)len {
      maxLength = len;
    }
    
    - (int)maximumLength {
      return maxLength;
    }
    
    - (NSString *)stringForObjectValue:(id)object {
      return (NSString *)object;
    }
    
    - (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error {
      *object = string;
      return YES;
    }
    
    - (BOOL)isPartialStringValid:(NSString **)partialStringPtr
       proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
              originalString:(NSString *)origString
       originalSelectedRange:(NSRange)origSelRange
            errorDescription:(NSString **)error {
        if ([*partialStringPtr length] > maxLength) {
            return NO;
        }
    
        if (![*partialStringPtr isEqual:[*partialStringPtr uppercaseString]]) {
          *partialStringPtr = [*partialStringPtr uppercaseString];
          return NO;
        }
    
        return YES;
    }
    
    - (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes {
      return nil;
    }
    
    @end