如何限制NSTextField文本长度并始终保持大写?
需要具有最多4个字符的文本限制的NSTextField,并且始终以大写显示,但无法找出实现该目的的好方法。我试图通过绑定与验证方法来做到这一点,但验证只有在控件失去第一响应者时才会被调用,这是不好的。如何限制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]];
}
}
但是,这肯定是不这样做的最佳方式。有什么更好的建议?
我一样格雷厄姆·李建议它工作正常,这里是自定义的格式化程序代码:
更新:添加了由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
您是否尝试过附加自定义NSFormatter
子类?
格雷姆李建议的自定义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;
}
,只有在iOS的存在与的UITextField。他正在使用NSTextField,并且此方法不存在。 –
在我评论了上面的例子,这是不好的:
// 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)用户将其粘贴到文本字段
2)的TextField将接受的字符的字符串
3)的TextField将格式化应用到“最后”字符在25长度的字符串
4)格式化程序的东西“最后“字符在25长度的字符串,忽略其它
5)文本字段将结束它25个字符,即使它限制为10
这是因为,我相信,只有第一种方法适用于键入NSTextField的“最后一个字符”。上面显示的第二种方法适用于键入NSTextField的“所有字符”。所以它不受“粘贴”漏洞的影响。
我刚刚发现了这个问题,试图破坏我的应用程序,我不是NSFormatter的专家,所以如果我错了,请纠正我。非常感谢你carlosb张贴该示例。它帮助了很多! :)
用户甚至不需要粘贴。用户定义的自定义键绑定(请参阅http://www.hcs.harvard.edu/~jrus/site/cocoa-text.html以获取详细信息)可以插入任何字符串以及基本多语言以外的单个代码点平面在Cocoa的两字节(UTF-16)意义上将是多个“字符”。 –
感谢那篇精彩的文章彼得! –
该实现采用了上述评论的几个建议。值得注意的是,它可以正确地连续更新绑定。
另外:
它正确地实现糊。
它包括一些笔记,关于如何在没有进一步子类化的情况下有效地在笔尖 中使用该类。
代码:
@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
值得赞赏 –
我需要一个格式转换为大写的斯威夫特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
}
}
你应该接受Grahams的回答,因为他指出你的方向正确!好工作寿! – Jab
感谢您花时间回来发布整个解决方案! –
我发现上面的代码有错误。有一个潜在的利用isPartialStringValid:newEditingString:errorDescription :.如果您在键盘上逐字输入文本到NSTextField中,则不会出现问题。但是,如果您将一个包含2个或更多字符的字符串粘贴到文本字段中,它将对输入的最后一个字符执行验证,但忽略所有先前输入的字符。这可能会导致插入更多字符而不允许插入到文本字段中。 下面我会发布更多的细节和解决方案(空间不足)。 –