在 uitextview 中限制字符数

我提供了一个文本视图来 tweet 一些字符串。

我正在应用以下方法将字符数限制为140个字符长度。

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
return [[textView text] length] <= 140;
}

除了第一个返回空间不工作的情况外,代码工作得很好。 假设我已经达到了140个字符的限制,因此该方法将给我 false,并且用户不能插入更多的字符,但在此之后,当我试图删除一些字符时,文本视图将表现为禁用。

所以问题是: “如何从 textview.text中删除字符或重新启用文本视图?”

80971 次浏览

You should be looking for an empty string instead, as the apple reference says

If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character.

I think the check you actually want to make is something like [[textView text] length] - range.length + text.length > 140, to account for cut/paste operations.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
return textView.text.length + (text.length - range.length) <= 140;
}

This accounts for users cutting text, or deleting strings longer than a single character (ie if they select and then hit backspace), or highlighting a range and pasting strings shorter or longer than it.

Swift 4.0 Version

 func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return textView.text.count + (text.count - range.length) <= 140
}

However you can use below working code also..

- (void)textViewDidChange:(UITextView *)textView{


NSInteger restrictedLength=140;


NSString *temp=textView.text;


if([[textView text] length] > restrictedLength){
textView.text=[temp substringToIndex:[temp length]-1];
}
}

ue this

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


int limit = 139;


return !([textField.text length]>limit && [string length] > range.length);


}

this will only enter 140 char and you can delete them if need

The Problem with some of the answer given above is, For example I have a text field and I have to set a limit of 15 characters input, then it stops after entering 15th Character. but they Don't allow to delete. That is the delete button also don't work. As I was facing the same problem. Came out with the solution , Given Below. Works Perfect for Me

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField.tag==6)
{
if ([textField.text length]<=30)
{
return YES;
}
else if([@"" isEqualToString:string])
{
textField.text=[textField.text substringToIndex:30 ];
}


return NO;
}
else
{
return YES;
}
}

I am having a text field, whose tag I have set "6" and I have restricted the max char limit = 30 ; works fine in every case

Write below code in textView:shouldChangeTextInRange:replacementText: method :

if ([textView.text length]>=3 && ![text isEqualToString:@""]) {
return NO;
}
return YES;

Use the following code.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if(text.length == 0)
{
return YES;
}
else if(self.txtViewComments.text.length > 255)
{
return NO;
}
else
{
return YES;
}
}

Swift:

// MARK: UITextViewDelegate


let COMMENTS_LIMIT = 255


func textView(textView: UITextView,  shouldChangeTextInRange range:NSRange, replacementText text:String ) -> Bool {
return count(comments.text) + (count(text) - range.length) <= COMMENTS_LIMIT;
}

Though I needed an if-else condition, so this worked for me:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
BOOL status = textView.text.length + (text.length - range.length) <= 15;
if (status)
{
[self.btnShare setEnabled:YES];
[self.btnShare setAlpha:1];
}
else
{
[self.btnShare setEnabled:NO];
[self.btnShare setAlpha:0.25];
}
return status;
}

Intially the button is set to disabled. But if you want user cant post an empty test, simply put a condition on button click:

- (void)btnShare_click:(id)sender
{
NSString *txt = [self.txtShare.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([txt length] == 0)
{
[self.btnShare setEnabled:NO];
[self.btnShare setAlpha:0.25f];
[[[UIAlertView alloc]initWithTitle:nil message:@"Please enter some text to share." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
return;
}
.
.
.
.
.
.
// rest of your code
}

@Tim Gostony 's Swift Version:

// restrict the input for textview to 500
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
return count(textView.text) + (count(text) - range.length) <= 500;
}

Here we go for best fit. Display number of characters left: 'n' characters left.

var charCount = 0;
let maxLength = 150
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {


if text == "" // Checking backspace
{
if textView.text.characters.count == 0
{
charCount = 0
characterCount.text = String(format: "%i Characters Left", maxLength - charCount)
return false
}
charCount = (textView.text.characters.count - 1)
characterCount.text = String(format: "%i Characters Left", maxLength - charCount)
return true
}
else
{
charCount = (textView.text.characters.count + 1)
characterCount.text = String(format: "%i Characters Left", maxLength - charCount)


if charCount >= maxLength + 1
{
charCount = maxLength
characterCount.text = String(format: "%i Characters Left", maxLength - charCount)
return false;
}
}
return true
}

Try this out:-

 func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
print("chars \(textView.text.count) \( text)")


if(textView.text.count > 20 && range.length == 0) {
print("Please summarize in 20 characters or less")
return false
}
return true
}

for swift 4:

//MARK:- TextView Delegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
//300 chars restriction
return textView.text.count + (text.count - range.length) <= 300
}

If you're also looking to be able to paste code up to the max character count, while cutting off overflow, and updating a count label:

let maxChar: Int
let countLabel: UILabel


func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let oldChar = textView.text.count - range.length
let oldRemainingChar = maxChar - oldChar
let newChar = oldChar + text.count
let newRemainingChar = maxChar - newChar
let replaceChar = newRemainingChar > 0 ? text.count : oldRemainingChar


if
let textRange = textView.textRange(for: range),
replaceChar > 0 || range.length > 0
{
textView.replace(textRange, withText: String(text.prefix(replaceChar)))
countLabel.text = String(describing: maxChar - textView.text.count)
}


return false
}

With the extension:

extension UITextInput {
func textRange(for range: NSRange) -> UITextRange? {
var result: UITextRange?


if
let start = position(from: beginningOfDocument, offset: range.location),
let end = position(from: start, offset: range.length)
{
result = textRange(from: start, to: end)


}


return result
}
}

With Swift 5 and iOS 12, try the following implementation of textView(_:shouldChangeTextIn:replacementText:) method that is part of the UITextViewDelegate protocol:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let rangeOfTextToReplace = Range(range, in: textView.text) else {
return false
}
let substringToReplace = textView.text[rangeOfTextToReplace]
let count = textView.text.count - substringToReplace.count + text.count
return count <= 10
}
  • The most important part of this code is the conversion from range (NSRange) to rangeOfTextToReplace (Range<String.Index>). See this video tutorial to understand why this conversion is important.
  • To make this code work properly, you should also set the textField's smartInsertDeleteType value to UITextSmartInsertDeleteType.no. This will prevent the possible insertion of an (unwanted) extra space when performing a paste operation.

The complete sample code below shows how to implement textView(_:shouldChangeTextIn:replacementText:) in a UIViewController:

import UIKit


class ViewController: UIViewController, UITextViewDelegate {


@IBOutlet var textView: UITextView! // Link this to a UITextView in Storyboard


override func viewDidLoad() {
super.viewDidLoad()


textView.smartInsertDeleteType = UITextSmartInsertDeleteType.no
textView.delegate = self
}


func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let rangeOfTextToReplace = Range(range, in: textView.text) else {
return false
}
let substringToReplace = textView.text[rangeOfTextToReplace]
let count = textView.text.count - substringToReplace.count + text.count
return count <= 10
}


}

🛑 Limit the overflow, not the entire text!

By returning false in the ...shouldChangeTextInRange... delegate method, you are actually limiting the entire text and handling the following situations could be so hard:

  • Copy and pasting
  • Select an area of the text and edit
  • Using auto-suggest
  • Using voice input (voice command or dictation)

So you can:

💡Just limit the text-overflow!

By removing extra characters:

textView.text = String(textView.text.prefix(140))

You can do it even on the fly! by putting this code inside the action of the textView or textField with the .editingChanged event.

Swift5:

let maxChars = 255


func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if maxChars - aTextView.text.count == 0 {
if range.length != 1 {
return false
}
}
return true
}

Here is the simplest solution for restricting user to enter number of character in UITextField or in UITextView in iOS swift

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
return textField.text!.count < limit ? true : false
}

Note: here limit can be anything from 1 to n as per your requirement, for e.g: if you working on phone number than limit value will be 10 if you want to apply same on UITextView just method will be change and at the place of textfield you will be use textview