2009-05-05 81 views
24

需要具有最多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]]; 
    } 
} 

但是,這肯定是不這樣做的最佳方式。有什麼更好的建議?

回答

43

我一樣格雷厄姆·李建議它工作正常,這裏是自定義的格式化程序代碼:

更新:添加了由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 
+1

你應該接受Grahams的回答,因爲他指出你的方向正確!好工作壽! – Jab

+1

感謝您花時間回來發佈整個解決方案! –

+1

我發現上面的代碼有錯誤。有一個潛在的利用isPartialStringValid:newEditingString:errorDescription :.如果您在鍵盤上逐字輸入文本到NSTextField中,則不會出現問題。但是,如果您將一個包含2個或更多字符的字符串粘貼到文本字段中,它將對輸入的最後一個字符執行驗證,但忽略所有先前輸入的字符。這可能會導致插入更多字符而不允許插入到文本字段中。 下面我會發布更多的細節和解決方案(空間不足)。 –

12

您是否嘗試過附加自定義NSFormatter子類?

-1

格雷姆李建議的自定義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; 
} 
+6

,只有在iOS的存在與的UITextField。他正在使用NSTextField,並且此方法不存在。 –

11

在我評論了上面的例子,這是不好的:

// 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張貼該示例。它幫助了很多! :)

+5

用戶甚至不需要粘貼。用戶定義的自定義鍵綁定(請參閱http://www.hcs.harvard.edu/~jrus/site/cocoa-text.html以獲取詳細信息)可以插入任何字符串以及基本多語言以外的單個代碼點平面在Cocoa的兩字節(UTF-16)意義上將是多個「字符」。 –

+0

感謝那篇精彩的文章彼得! –

9

該實現採用了上述評論的幾個建議。值得注意的是,它可以正確地連續更新綁定。

另外:

  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 
+0

值得讚賞 –

0

我需要一個格式轉換爲大寫的斯威夫特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 
    } 
}