Limit number of characters in uitextview
Asked Answered
C

18

87

I am giving a text view to tweet some string .

I am applying the following method to restrict the number of characters to 140 in length.

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

The code is working nicely except the first condition that backspace is not working. suppose that I have reached the limit of 140 characters so that the method will give me false and the user can not insert more characters but after that when I try to delete some characters the text view behave as it is disabled .

So the question is: "How to delete characters from textview.text or re-enable the text view?"

Crater answered 22/3, 2010 at 12:45 Comment(2)
Check this answerFlorinda
@Florinda https://mcmap.net/q/243151/-how-text-length-of-a-uitextview-can-be-fixedRockey
C
68

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.

Cheerful answered 22/3, 2010 at 12:50 Comment(6)
thanks but can be more specific I can not understand the technical specification of appleCrater
just tell me what should I put in the first condition . I am gooling for this for almost 4 hoursCrater
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.Cheerful
[[textView text] length] - range.length + text.length > 140 should be [[textView text] length] - range.length + text.length < 140Dday
Kuzon, not if you are returning true as default and want to return false if you exceed the 140 char limit.Sydneysydnor
In this case, you will count also the new line ... I would prefer something like this: NSMutableString *newString = [textView.text mutableCopy]; [newString replaceCharactersInRange:range withString:text]; if ( [newString length] <= 140) { .... if you dont want to count the new line you can simply manipulate newString ....Seger
M
179
- (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
}
Mlle answered 27/11, 2010 at 0:26 Comment(5)
is this an IBAction? how do I connect it to the TextView in question as user types?Menendez
Gotta make your view controller a UITextViewDelegate and then hook up file's owner as the text view's delegateMlle
I thought it would be more fair to ask a follow up question: I am having issues with having two UITextviews that need character limits in the same controller. Will you please help? #25272913Menendez
This should be marked as the correct response. Thank youJujitsu
This will cause a crash if you paste a text that is too long to fit (therefore false will be returned) and then you will try to undo the action.Leprosy
C
68

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.

Cheerful answered 22/3, 2010 at 12:50 Comment(6)
thanks but can be more specific I can not understand the technical specification of appleCrater
just tell me what should I put in the first condition . I am gooling for this for almost 4 hoursCrater
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.Cheerful
[[textView text] length] - range.length + text.length > 140 should be [[textView text] length] - range.length + text.length < 140Dday
Kuzon, not if you are returning true as default and want to return false if you exceed the 140 char limit.Sydneysydnor
In this case, you will count also the new line ... I would prefer something like this: NSMutableString *newString = [textView.text mutableCopy]; [newString replaceCharactersInRange:range withString:text]; if ( [newString length] <= 140) { .... if you dont want to count the new line you can simply manipulate newString ....Seger
M
19

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
}
Mallee answered 22/12, 2017 at 7:28 Comment(0)
D
11

🛑 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.

Decoration answered 22/10, 2019 at 15:52 Comment(1)
Placing this in the UITextView delegate's func textViewDidChange(_ textView: UITextView) {...} works beautifully, only cropping the end of a long pasted textBorges
S
10

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];
    }
}
Sampler answered 5/9, 2011 at 13:11 Comment(1)
Generally not the preferred way to edit something after it has been set. Better checking this in "shouldChangeCharactersInRange" to prevent text from being edited.Organism
A
7

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
    }

}
Aquilar answered 19/3, 2019 at 23:5 Comment(0)
W
5

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;
}
Wagonage answered 3/12, 2014 at 8:22 Comment(2)
countElements has been replaced by count in swift 1.2Houseline
and in Swift 2 you have to to textView.text.characters.count or whatever encoding you want: developer.apple.com/swift/blog/?id=30Glucosuria
L
4

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

Luckin answered 5/9, 2011 at 13:19 Comment(0)
M
2

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
}
Michaelmas answered 12/4, 2015 at 11:21 Comment(0)
H
2

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
}
Hixon answered 16/8, 2016 at 10:54 Comment(0)
S
1

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

Sanjak answered 21/3, 2014 at 14:38 Comment(0)
A
1

@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;
}
Ahron answered 24/8, 2015 at 18:47 Comment(0)
D
1

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
}
Durning answered 16/11, 2015 at 23:19 Comment(0)
T
1

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
    }
}
Thickening answered 8/11, 2018 at 20:15 Comment(0)
D
0

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

if ([textView.text length]>=3 && ![text isEqualToString:@""]) {
    return NO;
}
return YES;
Dessau answered 21/6, 2014 at 16:14 Comment(0)
C
0

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;
    }
}
Carleycarli answered 12/9, 2014 at 10:22 Comment(1)
if condition is used for deleting characters from the text viewCarleycarli
M
0

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
}
Mold answered 18/5, 2020 at 21:28 Comment(0)
F
0

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

Forcier answered 8/10, 2020 at 11:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.