Ограничение длины вставляемой строки в UITextView или UITextField - PullRequest
6 голосов
/ 16 июля 2009

Проблема ограничения строк, которые непосредственно вводятся в UITextView или UITextField, решалась в SO раньше:

Однако теперь с OS 3.0 копирование и вставка становится проблемой, так как решения в приведенных выше вопросах SO не препятствуют вставке дополнительных символов (т.е. вы не можете ввести более 10 символов в поле, настроенное с помощью выше решения, но вы можете легко вставить 100 символов в одно поле).

Есть ли способы предотвращения переполнения вставленной строки и вставленной строки?

Ответы [ 8 ]

10 голосов
/ 17 июля 2009

Мне удалось ограничить введенный и вставленный текст, следуя методу textViewDidChange: в протоколе UITextViewDelegate.

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

Но я все еще рассматриваю этот вид уродливого хака, и, похоже, Apple должна была предоставить какое-то свойство "maxLength" UITextFields и UITextViews.

Если кто-нибудь знает о лучшем решении, пожалуйста, сообщите.

7 голосов
/ 05 августа 2009

По моему опыту просто реализовать метод делегата:

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

работает с оклейкой. Вся вставленная строка попадает в аргумент replaceString:. Просто проверьте его длину, и если он длиннее вашей максимальной длины, просто верните NO из этого метода делегата. Это не приводит к вставке ничего. В качестве альтернативы вы можете подстроковать его, как предложено в предыдущем ответе, но это работает, чтобы вообще предотвратить вставку, если она слишком длинная, если вы этого хотите.

6 голосов
/ 18 апреля 2014

Изменение текста после его вставки в 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;
}
0 голосов
/ 19 апреля 2018

Этот код не позволит пользователю вводить больше символов, чем maxCharacters. Команда вставки ничего не сделает, если вставленный текст превысит этот предел.

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    let newText = (textView.text as NSString).replacingCharacters(in: range, with: text)
    return newText.count <= maxCharacters;
}
0 голосов
/ 08 февраля 2017

Вы можете узнать вставленную строку, если отметите 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 голосов
/ 25 января 2016

Кроме того, длина строки, как в «[длина строки]», это одно, но в определенной кодировке часто приходится сокращать количество байтов. Мне нужно было урезать ввод и вставку в 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 голосов
/ 10 мая 2012
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

  if(string.length>10){
    return NO;
  }
  return YES;
}
0 голосов
/ 17 июля 2009

Один из ответов в первом вопросе, на который вы ссылались выше, должен работать, а именно, использовать что-то вроде

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

чтобы следить за изменениями текста в UITextField и сокращать его при необходимости.

...