Frage

Sie benötigen einen NSTextField mit einem Textgrenze von maximal 4 Zeichen haben und zeigt immer in Großbuchstaben kann aber nicht eine gute Art und Weise zu erreichen, dass herauszufinden. Ich habe versucht, es durch eine Bindung mit einem Validierungsverfahren zu tun, aber die Validierung wird nur aufgerufen, wenn die Kontrolle verliert Ersthelfer und das ist nicht gut.

Temporär habe ich es arbeiten, indem sie die Benachrichtigung NSControlTextDidChangeNotification auf das Textfeld zu beobachten ist und sie rufen Sie die Methode:

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

Aber das ist sicherlich nicht der beste Weg, es zu tun. Alle besseren Vorschlag?

War es hilfreich?

Lösung

Ich tat, wie Graham Lee vorgeschlagen, und es funktioniert gut, hier ist der benutzerdefinierte Formatter-Code:

AKTUALISIERT: hinzugefügt Fix berichtet von Dave Gallagher. Dank!

@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

Andere Tipps

Im obigen Beispiel, wo ich bemerkte, das ist schlecht:

// Don't use:
- (BOOL)isPartialStringValid:(NSString *)partialString
            newEditingString:(NSString **)newString
            errorDescription:(NSString **)error
{
    if ((int)[partialString length] > maxLength)
    {
        *newString = nil;
        return NO;
    }
}

Verwenden Sie diese (oder so ähnlich) statt:

// Good to use:
- (BOOL)isPartialStringValid:(NSString **)partialStringPtr
       proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
              originalString:(NSString *)origString
       originalSelectedRange:(NSRange)origSelRange
            errorDescription:(NSString **)error
{
    int size = [*partialStringPtr length];
    if ( size > maxLength )
    {
        return NO;
    }
    return YES;
}

Beide sind NSFormatter Methoden. Der erste Teil hat ein Problem. Sagen Sie bitte Texteingabe zu 10 Zeichen. Wenn Sie Zeichen in einer Eins-zu-eins in eine NSTextField geben, es wird gut funktionieren, und verhindert, dass Benutzer über 10 Zeichen gehen.

Wenn jedoch ein Benutzer war auf Paste eine Reihe von, sagen wir, 25 Zeichen in das Textfeld, was etwas ist, passieren wird wie folgt aus:

1) Der Benutzer wird Einfügen in TextField-

2) TextField- die Zeichenfolge akzeptieren

3) TextField- die Formatierer auf das „letzte“ Zeichen in der 25-Zeichenfolge gilt

4) Formatter tut Zeug zum "letzten" Zeichen in der 25-Zeichenfolge, um den Rest zu ignorieren

5) TextField- wird sich mit 25 Zeichen in das enden, auch wenn es auf 10 begrenzt ist.

Das ist, weil, wie ich glaube, nur die erste Methode zum „allerletzten Zeichen“ gilt in eine NSTextField getippt. Das zweite Verfahren, das oben gezeigt, gilt für „alle Zeichen“ eingegeben in das NSTextField. So ist es immun gegen die „Einfügen“ zu nutzen.

Ich entdecken dies gerade jetzt versucht, meine Anwendung zu brechen, und ist kein Experte auf NSFormatter, also bitte korrigiert mich wenn ich falsch liege. Und sehr viel Dank an Sie carlosb für dieses Beispiel veröffentlichen. Es hat mir sehr geholfen! :)

Haben Sie versucht, eine benutzerdefinierte NSFormatter Unterklasse Anbringen?

nimmt Diese Implementierung einige der Vorschläge oben kommentiert. Insbesondere funktioniert es einwandfrei mit kontinuierlich aktualisierten Bindungen.

Zusätzlich:

  1. Es implementiert Paste richtig.

  2. Es enthält einige Hinweise, wie in einer Feder, die Klasse effektiv zu nutzen ohne weitere Unterklassen.

Der Code:

@interface BPPlainTextFormatter : NSFormatter {
    NSInteger _maxLength;
}


/*

 Set the maximum string length. 

 Note that to use this class within a Nib:
 1. Add an NSFormatter as a Custom Formatter.
 2. In the Identity inspector set the Class to BPPlainTextFormatter
 3. In user defined attributes add Key Path: maxLength Type: Number Value: 30

 Note that rather than attaching formatter instances to individual cells they
 can be positioned in the nib Objects section and referenced by numerous controls.
 A name, such as Plain Text Formatter 100, can  be used to identify the formatters max length.

 */
@property NSInteger maxLength;

@end


@implementation BPPlainTextFormatter
@synthesize maxLength = _maxLength;

- (id)init
{
    if(self = [super init]){
        self.maxLength = INT_MAX;
    }

    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    // support Nib based initialisation
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.maxLength = INT_MAX;
    }

    return self;
}

#pragma mark -
#pragma mark Textual Representation of Cell Content

- (NSString *)stringForObjectValue:(id)object
{
    NSString *stringValue = nil;
    if ([object isKindOfClass:[NSString class]]) {

        // A new NSString is perhaps not required here
        // but generically a new object would be generated
        stringValue = [NSString stringWithString:object];
    }

    return stringValue;
}

#pragma mark -
#pragma mark Object Equivalent to Textual Representation

- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error
{
    BOOL valid = YES;

    // Be sure to generate a new object here or binding woe ensues
    // when continuously updating bindings are enabled.
    *object = [NSString stringWithString:string];

    return valid;
}

#pragma mark -
#pragma mark Dynamic Cell Editing

- (BOOL)isPartialStringValid:(NSString **)partialStringPtr
       proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
              originalString:(NSString *)origString
       originalSelectedRange:(NSRange)origSelRange
            errorDescription:(NSString **)error
{
    BOOL valid = YES;

    NSString *proposedString = *partialStringPtr;
    if ([proposedString length] > self.maxLength) {

        // The original string has been modified by one or more characters (via pasting).
        // Either way compute how much of the proposed string can be accommodated.
        NSInteger origLength = origString.length;
        NSInteger insertLength = self.maxLength - origLength;

        // If a range is selected then characters in that range will be removed
        // so adjust the insert length accordingly
        insertLength += origSelRange.length;

        // Get the string components
        NSString *prefix = [origString substringToIndex:origSelRange.location];
        NSString *suffix = [origString substringFromIndex:origSelRange.location + origSelRange.length];
        NSString *insert = [proposedString substringWithRange:NSMakeRange(origSelRange.location, insertLength)];

#ifdef _TRACE

        NSLog(@"Original string: %@", origString);
        NSLog(@"Original selection location: %u length %u", origSelRange.location, origSelRange.length);

        NSLog(@"Proposed string: %@", proposedString);
        NSLog(@"Proposed selection location: %u length %u", proposedSelRangePtr->location, proposedSelRangePtr->length);

        NSLog(@"Prefix: %@", prefix);
        NSLog(@"Suffix: %@", suffix);
        NSLog(@"Insert: %@", insert);
#endif

        // Assemble the final string
        *partialStringPtr = [[NSString stringWithFormat:@"%@%@%@", prefix, insert, suffix] uppercaseString];

        // Fix-up the proposed selection range
        proposedSelRangePtr->location = origSelRange.location + insertLength;
        proposedSelRangePtr->length = 0;

#ifdef _TRACE

        NSLog(@"Final string: %@", *partialStringPtr);
        NSLog(@"Final selection location: %u length %u", proposedSelRangePtr->location, proposedSelRangePtr->length);

#endif
        valid = NO;
    }

    return valid;
}

@end

ich brauchte eine Formatter zu konvertieren für Swift 4. Bezug auf Großbuchstaben ich es hier aufgenommen haben:

import Foundation

class UppercaseFormatter : Formatter {

    override func string(for obj: Any?) -> String? {
        if let stringValue = obj as? String {
            return stringValue.uppercased()
        }
        return nil
    }

    override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
        obj?.pointee = string as AnyObject
        return true
    }
}

Der Brauch NSFormatter dass Graham Lee vorgeschlagen, ist der beste Ansatz.

Ein einfaches Flickschusterei wäre Ihre View-Controller einzustellen, wie die Delegierten Textfeld dann einfach jede bearbeiten blockieren, die die Länge länger als 4 nicht-Groß- oder macht beinhaltet:

- (BOOL)textField:(UITextField *)textField
    shouldChangeCharactersInRange:(NSRange)range
    replacementString:(NSString *)string
{
    NSMutableString *newValue = [[textField.text mutableCopy] autorelease];
    [newValue replaceCharactersInRange:range withString:string];

    NSCharacterSet *nonUppercase =
        [[NSCharacterSet uppercaseLetterCharacterSet] invertedSet];
    if ([newValue length] > 4 ||
        [newValue rangeOfCharacterFromSet:nonUppercase].location !=
            NSNotFound)
    {
       return NO;
    }

    return YES;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top