需要一个NSTextField,其文本限制最多为4个字符,并且始终以大写字母显示,但无法找到实现该目标的好方法。我试图通过与验证方法的绑定来实现它,但只有当控件失去第一个响应者时才会调用验证,这是不合适的。

我暂时通过在文本字段上观察通知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]];
  }
}

但这肯定不是最好的方法。还有更好的建议吗?

有帮助吗?

解决方案

我做了Graham Lee的建议并且工作正常,这是自定义格式化代码:

更新:添加Dave Gallagher报道的修复程序。谢谢!

@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

其他提示

在我评论的上述示例中,这很糟糕:

// 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,它将正常工作并防止用户超过10个字符。

但是,如果用户要在文本字段中粘贴一串(例如25个字符),那么会发生以下情况:

1)用户将粘贴到TextField

2)TextField将接受字符串

3)TextField将格式化程序应用于“last” 25长字符串中的字符

4)格式化程序对“最后”的内容进行填充。 25长度字符串中的字符,忽略其余字符

5)TextField最终会有25个字符,即使它只限于10个。

这是因为,我认为,第一种方法仅适用于“最后一个字符”。键入NSTextField。上面显示的第二种方法适用于“所有字符”。键入NSTextField。因此它不受“粘贴”的影响。利用。

我刚刚发现这个试图破坏我的应用程序,而且我不是NSFormatter的专家,所以如果我错了请纠正我。非常感谢你 carlosb 发布这个例子。它帮了很多! :)

您是否尝试过附加自定义 NSFormatter 子类?

此实施采用了上述评论中的几条建议。值得注意的是,它可以连续更新绑定。

另外:

  1. 它正确实现了粘贴。

  2. 它包含一些关于如何在笔尖中有效使用该类的注释 没有进一步的子类化。

  3. 代码:

    @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
    

我需要一个Formatter才能转换为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
    }
}

Graham Lee建议的自定义NSFormatter是最好的方法。

一个简单的方法是将视图控制器设置为文本字段的委托,然后阻止任何涉及非大写的编辑或使长度超过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;
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top