كيفية تحديد طول نص NSTextField والاحتفاظ به دائمًا بأحرف كبيرة؟
-
05-07-2019 - |
سؤال
تحتاج إلى الحصول على 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]];
}
}
لكن هذه بالتأكيد ليست أفضل طريقة للقيام بذلك.أي اقتراح أفضل؟
المحلول
وفعلت كما اقترح غراهام لي وأنه يعمل بشكل جيد، وهنا رمز المنسق مخصص:
ومحدث: الإصلاح واضاف ذكرت من قبل ديف غالاغر. شكرا!
@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 بتطبيق المنسق على الحرف "الأخير" في السلسلة المكونة من 25 طولًا
4) يقوم المنسق بإجراء أشياء على الحرف "الأخير" في السلسلة المكونة من 25 طولًا، متجاهلاً الباقي
5) سينتهي TextField بـ 25 حرفًا، على الرغم من أنه يقتصر على 10.
هذا لأنني أعتقد أن الطريقة الأولى تنطبق فقط على "الحرف الأخير" المكتوب في NSTextField.تنطبق الطريقة الثانية الموضحة أعلاه على "كافة الأحرف" المكتوبة في NSTextField.لذلك فهو محصن ضد استغلال "اللصق".
لقد اكتشفت هذا للتو أثناء محاولتي تعطيل طلبي، ولست خبيرًا في NSFormatter، لذا يرجى تصحيحي إذا كنت مخطئًا.ولكم جزيل الشكر com.carlosb لنشر هذا المثال.لقد ساعد كثيرا!:)
هل حاولت ربط فئة فرعية NSFormatter
مخصصة؟
يعتمد هذا التنفيذ العديد من الاقتراحات التي تم التعليق عليها أعلاه.والجدير بالذكر أنه يعمل بشكل صحيح مع تحديث الارتباطات بشكل مستمر.
فضلاً عن ذلك:
ينفذ اللصق بشكل صحيح.
ويشمل بعض الملاحظات حول كيفية استخدام الفصل بشكل فعال في NIB دون مزيد من التصنيف الفرعي.
الرمز:
@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
}
}
والعادة NSFormatter أن غراهام لي اقترح هو أفضل نهج.
وA حل مشكلة بسيطة سيكون لتعيين تحكم وجهة نظركم مندوبا مجال النص فقط ثم منع أي تعديل ينطوي على غير كبيرة أو يجعل طول أطول من 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;
}