Re: NSTextField, maximum string length, bindings and NSFormatter
Re: NSTextField, maximum string length, bindings and NSFormatter
- Subject: Re: NSTextField, maximum string length, bindings and NSFormatter
- From: Keary Suska <email@hidden>
- Date: Thu, 24 Oct 2013 17:21:19 -0600
On Oct 24, 2013, at 9:01 AM, email@hidden wrote:
> Have I missed something or is access to a decent NSFormatter subclass to handle NSTextField string length limiting troublesome?
>
> There is some form on this:
> http://stackoverflow.com/questions/827014/how-to-limit-nstextfield-text-length-and-keep-it-always-upper-case/827598#827598
> http://www.cocoabuilder.com/archive/cocoa/184885-nsformatter-interfering-with-bindings-continuous-update.html
>
> However the solutions provided have issues, some with bindings, and most suggestions don't handle pasted text well.
>
> I am currently using the following. Comments, suggested improvements etc welcome.
I recall that this can be tricky. Below is a method that I dug up from an old NSFormatter subclass that handles both min/max lengths, non-stored delimiters and filtering to acceptable characters, which I recall is also paste-proof. I can send the whole class files if you care.
- (BOOL)isPartialStringValid:(NSString **)partial proposedSelectedRange:(NSRange *)proposedRange originalString:(NSString *)original originalSelectedRange:(NSRange)originalRange
errorDescription:(NSString **)errorDescription
{
// ignore empty
if( ! [*partial length] ) return YES;
// we want a mutable string
NSMutableString *newString = [NSMutableString stringWithString:*partial];
// if single deletion, adjust to ignore delimiters by changing what is expected to be deleted
if( proposedRange->location == originalRange.location && originalRange.length == 1 && originalRange.location > 0 )
{
while( ! [allowedCharacters characterIsMember:[original characterAtIndex:proposedRange->location]] )
{
proposedRange->location--;
}
// re-adjust string & change orig
[newString deleteCharactersInRange:NSMakeRange( proposedRange->location, originalRange.location - proposedRange->location )];
originalRange.location = proposedRange->location;
}
NSUInteger proposedLocation = proposedRange->location;
// strip down--this will include removing delimiters & updating location
[newString setString:[self filterToAllowed:newString location:&proposedLocation]];
// are we too long?
if( maxLength > 0 && [newString length] > maxLength )
{
// delete from added
NSUInteger delta = [newString length] - maxLength;
proposedLocation -= delta;
[newString deleteCharactersInRange:NSMakeRange( proposedLocation, delta )];
NSBeep();
}
// determine the string being added: if deletion or filter actions make orig before proposed, consider empty added
NSString *addedString = nil;
if( originalRange.location < proposedLocation )
addedString = [newString substringWithRange:NSMakeRange( originalRange.location, proposedLocation-originalRange.location )];
if( [addedString length] )
{
addedString = [self caseFoldString:addedString precursor:[newString substringToIndex:originalRange.location]];
[newString replaceCharactersInRange:NSMakeRange( originalRange.location, proposedLocation-originalRange.location ) withString:addedString];
}
else if( originalRange.location < [newString length] )
{
// if no added--i.e. deletion or all added was filtered out--only check case folding if not at end
addedString = [self caseFoldString:[newString substringWithRange:NSMakeRange( originalRange.location, 1 )] precursor:[newString substringToIndex:originalRange.location]];
[newString replaceCharactersInRange:NSMakeRange( originalRange.location, 1 ) withString:addedString];
}
// now format & check changed
NSString *finalString = [self formatString:newString location:&proposedLocation];
BOOL valid = [*partial isEqualToString:finalString];
// update proposed location
proposedRange->location = proposedLocation;
// check completions
if( completions )
{
NSUInteger i, count = [completions count];
for( i=0; i<count; i++ )
{
NSString *proposed = [completions objectAtIndex:i];
if( [proposed rangeOfString:finalString options:NSCaseInsensitiveSearch].location == 0 && [proposed length] >= [finalString length] )
{
*proposedRange = NSMakeRange( [finalString length], [proposed length] - [finalString length] );
finalString = proposed;
break;
}
}
}
// always set
*partial = finalString;
return valid;
}
HTH,
Keary Suska
Esoteritech, Inc.
"Demystifying technology for your home or business"
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden