2009-07-16 12 views
5

Problem ograniczania łańcuchów, które są wprowadzane bezpośrednio do UITextView lub UITextField została skierowana na SO przed:Ograniczanie wklejane długość ciągu w UITextView lub UITextField

jednak teraz w wersji 3.0 kopiowanie i wklejanie staje się problemem, ponieważ rozwiązania w powyższych pytaniach SO nie zapobiegają wklejaniu dodatkowych znaków (tj. nie można wpisać więcej niż 10 znaków do pola skonfigurowanego z h powyższe rozwiązania, ale możesz łatwo wkleić 100 znaków do tego samego pola).

Czy istnieje sposób zapobiegania wprowadzaniu bezpośrednio ciągu znaków i przepełnienie ciągu wklejonego?

+0

możliwe duplikat [iPhone SDK: Ustaw Character Max długość TextField] (http://stackoverflow.com/questions/433337/iphone-sdk-set-max-character-length-textfield) – JosephH

Odpowiedz

0

Jedną z odpowiedzi na pierwsze pytanie związane powyżej powinny działać, a mianowicie za pomocą coś jak

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

obserwować zmiany do tekstu w UITextField i skrócić go w razie potrzeby.

+1

W tym szczególnym przypadku, , zgodne z UITextViewDelegate (lub UITextFieldDelegate) jest lepsze niż toczenie własnego powiadomienia (i łatwiejsze do debugowania). I prawdopodobnie powinienem opublikować, jak faktycznie usunąłem ten problem ... –

10

Udało mi się ograniczyć wprowadzony i wklejony tekst, zgodnie z metodą textViewDidChange: w ramach protokołu UITextViewDelegate.

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

Ale nadal uważam ten rodzaj brzydki hack, i wydaje się, Apple powinno dostarczyły jakiś „MaxLength” własność UITextFields i UITextViews.

Jeśli ktoś jest świadomy lepszego rozwiązania, proszę powiedzieć.

+0

Powoduje to awarię, gdy "Cofam". Zobacz moją odpowiedź na "nieco" lepsze rozwiązanie. –

7

Z mojego doświadczenia tylko realizacji sposobu Delegat:

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

prace z wklejeniem. Cały wklejony ciąg pojawia się w argumencie replacementString:. Sprawdź jego długość, a jeśli jest dłuższa niż maksymalna długość, po prostu zwróć NIE z tej delegowanej metody. To nie powoduje wklejenia niczego. Alternatywnie możesz podciąć go tak, jak sugerowała wcześniejsza odpowiedź, ale to działa, aby w ogóle nie wklejać, jeśli jest zbyt długi, jeśli tego chcesz.

+0

Wklejanie, jeśli tekst jest zbyt długi, nie jest idealne –

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

    if(string.length>10){ 
    return NO; 
    } 
    return YES; 
} 
+2

Ten nie działa! – fengd

5

Zmiana tekstu po jego wstawieniu w tekstViewViewDidChange: powoduje awarię aplikacji, jeśli użytkownik naciśnie "Cofnij" po wklejeniu.

Trochę się bawiłem i udało mi się znaleźć skuteczne rozwiązanie. Zasadniczo logika jest taka: nie zezwalaj na wklejanie, jeśli całkowita długość jest większa niż maksymalna liczba znaków, wykryj ilość, która jest nadpisana, i wstaw tylko częściowy ciąg znaków.

Używając tego rozwiązania, Twój karton i menedżer cofania działają zgodnie z oczekiwaniami.

- (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

Można to zrobić bez dispatch_async. dispatch_async spowoduje awarię systemu iOS8 z nową funkcją naprzód. Wkrótce przerobię kod. –

0

Ponadto, długość łańcucha, jak w „[]” długości łańcucha jest jedno, ale często trzeba obciąć do liczby bajtów w pewnym kodowania. Musiałem skrócić typowanie i wklejenie do UITextView do maksymalnej liczby UTF8, oto jak to zrobiłem. (Robienie czegoś podobnego w UITextField jest ćwiczeniem dla czytelnika.)

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

dzięki! ten fragment pomógł mi znaleźć prawidłowy sposób zastępowania tekstu w polu tekstowym! Zrobiłem szybką wersję 3: https://gist.github.com/Blackjacx/2198d86442ec9b9b05c0801f4e392047 – blackjacx

0

Można wiedzieć wklejony ciąg sprawdzając za string.length w shouldChangeCharactersIn range: metoda delegowania

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 
} 
Powiązane problemy