限制直接输入到 UITextView 或 UITextField 的字符串的问题之前已经在 SO 上解决:
但是现在使用 OS 3.0 复制和粘贴成为一个问题,因为上述 SO 问题中的解决方案不会阻止粘贴其他字符(即,您不能在使用上述解决方案配置的字段中键入超过 10 个字符,但是您可以轻松地将 100 个字符粘贴到同一字段中)。
有没有办法防止直接输入的字符串和粘贴的字符串溢出?
限制直接输入到 UITextView 或 UITextField 的字符串的问题之前已经在 SO 上解决:
但是现在使用 OS 3.0 复制和粘贴成为一个问题,因为上述 SO 问题中的解决方案不会阻止粘贴其他字符(即,您不能在使用上述解决方案配置的字段中键入超过 10 个字符,但是您可以轻松地将 100 个字符粘贴到同一字段中)。
有没有办法防止直接输入的字符串和粘贴的字符串溢出?
我能够通过符合 UITextViewDelegate 协议中的 textViewDidChange: 方法来限制输入和粘贴的文本。
- (void)textViewDidChange:(UITextView *)textView
{
if (textView.text.length >= 10)
{
textView.text = [textView.text substringToIndex:10];
}
}
但我仍然认为这种丑陋的 hack,似乎 Apple 应该提供 UITextFields 和 UITextViews 的某种“maxLength”属性。
如果有人知道更好的解决方案,请告诉。
根据我的经验,只是实现委托方法:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
适用于粘贴。整个粘贴的字符串出现在 replacementString: 参数中。只需检查它的长度,如果它比你的最大长度长,那么就从这个委托方法返回 NO。这会导致无法粘贴任何内容。或者,您可以像之前建议的答案那样对其进行子串化,但这可以防止粘贴太长,如果这是您想要的。
在 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;
}
此代码不会让用户输入比 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;
}
您在上面链接的第一个问题中的一个答案应该有效,即使用类似
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitTextField:) name:@"UITextFieldTextDidChangeNotification" object:myTextField];
观察 UITextField 中文本的变化,并在适当的时候缩短它。
此外,'[string length]' 中的字符串长度是一回事,但通常需要在某种编码中截断为字节数。我需要将键入和粘贴到 UITextView 中的内容截断为最大 UTF8 计数,这就是我的做法。(对 UITextField 做类似的事情对读者来说是一个练习。)
NSString+截断UTF8.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]);
// }
我的视图控制器.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;
}
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
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if(string.length>10){
return NO;
}
return YES;
}