Limit the length of the insertion string in a UITextView or UITextField

The problem of restricting strings directly entered into a UITextView or UITextField has been addressed to SO before:

However, now copying and pasting OS 3.0 is becoming a problem, since the solutions in the above SO questions do not prevent the insertion of additional characters (i.e. you cannot enter more than 10 characters in the field configured with the above solutions, but you can easily insert 100 characters in the same field).

Is there a way to prevent a line from directly entering and pasting overflow rows?

+5
source share
7 answers

I managed to limit the entered and pasted text by executing the textViewDidChange: method in the UITextViewDelegate protocol.

- (void)textViewDidChange:(UITextView *)textView
{
    if (textView.text.length >= 10)
    {
        textView.text = [textView.text substringToIndex:10];
    }
}

But I'm still considering this kind of ugly hack, and it looks like Apple should have provided some sort of “maxLength” property for UITextFields and UITextViews.

If anyone knows of a better solution, please tell me.

+10
source

In my experience, I just implement the delegate method:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

. replaceString:. , , NO . . , , , , , , .

+7

textViewDidChange: , "" .

. : , , , , , .

, .

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSInteger newLength = textView.text.length - range.length + text.length;

    if (newLength > MAX_LENGTH) {
        NSInteger overflow = newLength - MAX_LENGTH;

        dispatch_async(dispatch_get_main_queue(), ^{
            UITextPosition *start = [textView positionFromPosition:nil offset:range.location];
            UITextPosition *end = [textView positionFromPosition:nil offset:NSMaxRange(range)];
            UITextRange *textRange = [textView textRangeFromPosition:start toPosition:end];
            [textView replaceRange:textRange withText:[text substringToIndex:text.length - overflow]];
        });
        return NO;
    }
    return YES;
}
+5

, , , , -

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitTextField:) name:@"UITextFieldTextDidChangeNotification" object:myTextField];

UITextField .

0

, , '[ ], - , . UITextView UTF8, . ( - UITextField - .)

NSString + TruncateUTF8.h

#import <Foundation/Foundation.h>
@interface NSString (TruncateUTF8)
- (NSString *)stringTruncatedToMaxUTF8ByteCount:(NSUInteger)maxCount;
@end

NSString + TruncateUTF8.m

#import "NSString+TruncateUTF8.h"
@implementation NSString (TruncateUTF8)
- (NSString *)stringTruncatedToMaxUTF8ByteCount:(NSUInteger)maxCount {
  NSRange truncatedRange = (NSRange){0, MIN(maxCount, self.length)};
  NSInteger byteCount;

  // subtract from this range to account for the difference between NSString's
  // length and the string byte count in utf8 encoding
  do {
    NSString *truncatedText = [self substringWithRange:truncatedRange];
    byteCount = [truncatedText lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    if (byteCount > maxCount) {
      // what do we subtract from the length to account for this excess count?
      // not the count itself, because the length isn't in bytes but utf16 units
      // one of which might correspond to 4 utf8 bytes (i think)
      NSUInteger excess = byteCount - maxCount;
      truncatedRange.length -= ceil(excess / 4.0);
      continue;
    }
  } while (byteCount > maxCount);

  // subtract more from this range so it ends at a grapheme cluster boundary
  for (; truncatedRange.length > 0; truncatedRange.length -= 1) {
    NSRange revisedRange = [self rangeOfComposedCharacterSequencesForRange:truncatedRange];
    if (revisedRange.length == truncatedRange.length)
      break;
  }

  return (truncatedRange.length < self.length) ? [self substringWithRange:truncatedRange] : self;
}
@end

// tested using:
//    NSString *utf8TestString = @"Hello world, Καλημέρα κόσμε, コンニチハ ∀x∈ℝ ıntəˈnæʃənəl ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ STARGΛ̊TE γνωρίζω გთხოვთ  ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช ሰማይ አይታረስ ንጉሥ አይከሰስ። ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ ░░▒▒▓▓██ ▁▂▃▄▅▆▇█";
//    NSString *truncatedString;
//    NSUInteger byteCount = [utf8TestString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
//    NSLog(@"length %d: %p %@", (int)byteCount, utf8TestString, utf8TestString);
//    for (; byteCount > 0; --byteCount) {
//        truncatedString = [utf8TestString stringTruncatedToMaxUTF8ByteCount:byteCount];
//        NSLog(@"truncate to length %d: %p %@ (%d)", (int)byteCount, truncatedString, truncatedString, (int)[truncatedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
//    }

MyViewController.m

#import "NSString+TruncateUTF8.h"
...
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)replacementText
{
  NSMutableString *newText = textView.text.mutableCopy;
  [newText replaceCharactersInRange:range withString:replacementText];

  // if making string larger then potentially reject
  NSUInteger replacementTextLength = replacementText.length;
  if (self.maxByteCount > 0 && replacementTextLength > range.length) {
    // reject if too long and adding just 1 character
    if (replacementTextLength == 1 && [newText lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > self.maxByteCount) {
      return NO;
    }

    // if adding multiple charaters, ie. pasting, don't reject altogether but instead return YES
    // to accept and truncate immediately after, see http://stackoverflow.com/a/23155325/592739
    if (replacementTextLength > 1) {
      NSString *truncatedText = [newText stringTruncatedToMaxUTF8ByteCount:self.maxByteCount]; // returns same string if truncation needed
      if (truncatedText != newText) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0LL), dispatch_get_main_queue(), ^{
          UITextPosition *replaceStart = [textView positionFromPosition:textView.beginningOfDocument offset:range.location];
          UITextRange *textRange = [textView textRangeFromPosition:replaceStart toPosition:textView.endOfDocument];
          [textView replaceRange:textRange withText:[truncatedText substringFromIndex:range.location]];

          self.rowDescriptor.value = (truncatedText.length > 0) ? truncatedText : nil;
        });
      }
    }
  }

  [self updatedFieldWithString:(newText.length > 0) ? newText : nil]; // my method
  return YES;
}
0

, string.length shouldChangeCharactersIn range:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if string.length > 1 {
        //pasted string
        // do you stuff like trim
    } else {
        //typed string
    }
    return true
}
0
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

  if(string.length>10){
    return NO;
  }
  return YES;
}
-1
source

All Articles