26

最大4文字のテキスト制限を持つNSTextFieldが必要であり、常に大文字で表示されますが、それを実現するための良い方法を理解することはできません。検証メソッドを使用したバインディングを介してそれを実行しようとしましたが、コントロールがファーストレスポンダーを失った場合にのみ検証が呼び出され、それは良くありません。

一時的に、テキストフィールドの通知NSControlTextDidChangeNotificationを観察し、メソッドを呼び出すことで機能させました。

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

しかし、これは確かにそれを行うための最良の方法ではありません。より良い提案はありますか?

4

7 に答える 7

49

Graham Leeが提案したように実行しましたが、正常に機能します。カスタムフォーマッタコードは次のとおりです。

更新:DaveGallagherによって報告された修正を追加しました。ありがとう!

@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
于 2009-05-06T14:56:59.383 に答える
13

NSFormatterカスタムサブクラスをアタッチしてみましたか?

于 2009-05-05T22:30:55.557 に答える
12

私がコメントした上記の例では、これは悪いことです:

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

代わりにこれ(またはそのようなもの)を使用してください:

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

どちらもNSFormatterメソッドです。最初のものには問題があります。テキスト入力を10文字に制限するとします。NSTextFieldに文字を1つずつ入力すると、正常に機能し、ユーザーが10文字を超えないようにします。

ただし、ユーザーがたとえば25文字の文字列をテキストフィールドに貼り付けると、次のようになります。

1)ユーザーはTextFieldに貼り付けます

2)TextFieldは文字列を受け入れます

3)TextFieldは、25の長さの文字列の「最後の」文字にフォーマッタを適用します

4)フォーマッタは、25の長さの文字列の「最後の」文字を処理し、残りは無視します。

5)TextFieldは、10文字に制限されていますが、最終的に25文字になります。

これは、最初のメソッドがNSTextFieldに入力された「最後の文字」にのみ適用されるためだと思います。上記の2番目の方法は、NSTextFieldに入力された「すべての文字」に適用されます。したがって、「貼り付け」エクスプロイトの影響を受けません。

私はこれをちょうど今私のアプリケーションを壊そうとしていることを発見しました、そしてNSFormatterの専門家ではないので、私が間違っているならば私を訂正してください。そして、その例を投稿してくれたcarlosbに感謝します。それはたくさん助けました!:)

于 2010-05-03T02:14:18.387 に答える
10

この実装は、上記でコメントしたいくつかの提案を採用しています。特に、バインディングを継続的に更新することで正しく機能します。

加えて:

  1. 貼り付けを正しく実装します。

  2. さらにサブクラス化せずにペン先でクラスを効果的に使用する方法に関するいくつかの注意事項が含まれています。

コード:

@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
于 2013-10-28T12:48:56.457 に答える
3

Swift 4の大文字に変換するには、フォーマッターが必要でした。参考までに、ここに含めました。

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
    }
}
于 2017-11-19T22:21:27.120 に答える
1

カルロスバルボサのスウィフトバージョンは、誰かがそれを必要とする場合に答えます。

使用例:

myTextField.formatter = CustomTextFieldFormatter(maxLength: 10, isUppercased: true)

class CustomTextFieldFormatter: Formatter {
    var maxLength: UInt
    var isUppercased: Bool
    
    init(maxLength: UInt, isUppercased: Bool) {
        self.maxLength = maxLength
        self.isUppercased = isUppercased
        super.init()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func string(for obj: Any?) -> String? {
        return obj as? String
    }
    
    override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
        obj?.pointee = string as AnyObject
        return true
    }
    
    override func isPartialStringValid(_ partialStringPtr: AutoreleasingUnsafeMutablePointer<NSString>, proposedSelectedRange proposedSelRangePtr: NSRangePointer?, originalString origString: String, originalSelectedRange origSelRange: NSRange, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
        
        if partialStringPtr.pointee.length > maxLength {
            return false
        }
        
        
        if isUppercased && partialStringPtr.pointee != partialStringPtr.pointee.uppercased as NSString {
            partialStringPtr.pointee = partialStringPtr.pointee.uppercased as NSString
            return false
        }
        
        return true
    }
    
    override func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key : Any]? = nil) -> NSAttributedString? {
        return nil
    }
}
于 2021-04-28T08:01:23.063 に答える
-5

GrahamLeeが提案したカスタムNSFormatterが最良のアプローチです。

簡単な手間は、View Controllerをテキストフィールドのデリゲートとして設定し、大文字以外の編集や長さを4より長くする編集をブロックすることです。

- (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;
}
于 2009-05-06T01:03:23.367 に答える