Objective c 如何限制NSTextField文本长度并始终保持大写?
需要一个文本限制为最多4个字符的NSTextField,并始终以大写形式显示,但无法找到一个实现此目标的好方法。我曾尝试通过绑定验证方法来实现这一点,但只有当控件失去第一响应程序时才会调用验证,这是不好的 暂时,我通过观察文本字段上的通知NSControlTextDidChangeNotification并让它调用以下方法使其工作:Objective c 如何限制NSTextField文本长度并始终保持大写?,objective-c,cocoa,user-interface,nstextfield,Objective C,Cocoa,User Interface,Nstextfield,需要一个文本限制为最多4个字符的NSTextField,并始终以大写形式显示,但无法找到一个实现此目标的好方法。我曾尝试通过绑定验证方法来实现这一点,但只有当控件失去第一响应程序时才会调用验证,这是不好的 暂时,我通过观察文本字段上的通知NSControlTextDidChangeNotification并让它调用以下方法使其工作: - (void)textDidChange:(NSNotification*)notification { NSTextField* textField = [
- (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]];
}
}
但这肯定不是最好的方法。有更好的建议吗?您是否尝试附加自定义的
NSFormatter
子类?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;
}
我按照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;
}
}
改用这个(或类似的东西):
这两种方法都是NSFormatter方法。第一个问题。假设您将文本输入限制为10个字符。如果在NSTEXT字段中逐个键入字符,它将正常工作,并防止用户超过10个字符
但是,如果用户将一个字符串粘贴到文本字段中,例如25个字符,则会发生如下情况:
1) 用户将粘贴到文本字段中
2) TextField将接受字符串
3) TextField将格式化程序应用于25长度字符串中的“最后一个”字符
4) 格式化程序将填充到25长度字符串中的“最后一个”字符,忽略其余字符
5) TextField将以25个字符结束,即使限制为10个字符
这是因为,我相信,第一种方法只适用于在NSTextField中键入的“最后一个字符”。上面显示的第二种方法适用于在NSTEXT字段中键入的“所有字符”。所以它对“粘贴”攻击免疫
我刚刚在尝试破解我的应用程序时发现了这个问题,我不是NSFormatter方面的专家,所以如果我错了,请纠正我。非常感谢您carlosb发布了这个示例。这帮了大忙!:) 这一实施采纳了上述几条建议。值得注意的是,它可以正确地处理不断更新的绑定 此外:
@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
我需要一个格式化程序将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
}
}
<代码>导入基础
类UppercaseFormatter:格式化程序{
覆盖func字符串(对象:有?->字符串{
如果let stringValue=obj as?字符串{
返回stringValue.uppercased()
}
归零
}
重写func getObjectValue(j:autoreleasingusafmtablepointer?,对于字符串:string,errorDescription error:autoreleasingusafmtablepointer?)->Bool{
obj?.pointee=字符串作为任意对象
返回真值
}
}
如果有人需要,快速回答
用法示例:
myTextField.formatter = CustomTextFieldFormatter(maxLength: 10, isUppercased: true)
class CustomTextFieldFormatter:格式化程序{
变量最大长度:UInt
变量大写:Bool
init(最大长度:UInt,大写:Bool){
self.maxLength=maxLength
self.isUppercased=isUppercased
super.init()
}
必需初始化?(编码器:NSCoder){
fatalError(“初始化(编码者:)尚未实现”)
}
覆盖func字符串(对象:有?->字符串{
返回obj作为字符串
}
重写func getObjectValue(j:autoreleasingusafmtablepointer?,对于字符串:string,errorDescription error:autoreleasingusafmtablepointer?)->Bool{
obj?.pointee=字符串作为任意对象
返回真值
}
重写函数isPartialStringValid(uPartialStringPtr:AutoreleasingUnsafeMutablePointer,proposedSelectedRange proposedSelRangePtr:NSRangePointer?,OriginalStringOrigString:String,originalSelectedRange origSelRange:NSRange,错误描述错误:AutoreleasingUnsafeMutablePointer?->Bool{
如果partialStringPtr.pointee.length>maxLength{
返回错误
}
如果isUppercased&&partialStringPtr.pointee!=partialStringPtr.pointee.uppercased为NSString{
partialStringPtr.pointee=partialStringPtr.pointee.UPPERCASE为NSString
返回错误
}
返回真值
}
重写func attributedString(对于obj:Any,使用defaultattributes attrs:[NSAttributedString.Key:Any]?=nil)->NSAttributedString{
归零
}
}
你应该接受格雷厄姆的回答,因为他为你指明了正确的方向!干得好!感谢您抽出时间回来发布整个解决方案!我发现上面的代码有一个错误。使用isPartialStringValid:newEditingString:errorDescription:存在潜在的攻击。如果在键盘上逐个字符地将文本输入NSTEXT字段,则不会出现任何问题。但是,如果将包含2个或更多字符的字符串粘贴到文本字段中,它将对最后输入的字符执行验证,但忽略以前输入的所有字符。这可能导致在文本字段中插入的字符超过允许的数量。下面我将发布更多详细信息和解决方案(此处空间不足)。请您编辑“-init{”以读取“-(id)init{”?将使复制/粘贴更容易。请记住,如果您从Interface Builder将自定义格式设置程序拖到文本字段上,您将需要使用-(id)initWithCoder:(NSCoder*)aDecoder而不是
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
}
}