• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag
 

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: NSTextField, maximum string length, bindings and NSFormatter
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


  • Follow-Ups:
    • Re: NSTextField, maximum string length, bindings and NSFormatter
      • From: "email@hidden" <email@hidden>
References: 
 >NSTextField, maximum string length, bindings and NSFormatter (From: "email@hidden" <email@hidden>)

  • Prev by Date: Spotlight importer don't work anymore under Mavericks (sandbox error)
  • Next by Date: operatingSystemVersionString replacement
  • Previous by thread: Re: NSTextField, maximum string length, bindings and NSFormatter
  • Next by thread: Re: NSTextField, maximum string length, bindings and NSFormatter
  • Index(es):
    • Date
    • Thread