Best Way to Limit Number of Characters in a Text Box?

What is the best way to limit the amount of characters a user can enter into a UITextField? Some of the online forums claim the call to func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) is the best way to limit the number of characters but I can't even get that function to be called in my .swift file.


A related question would be how can I limit the type of character that a user enters into a textbox?

Tried this?

xtView, shouldChangeTextInRange
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;

}

KMT how do I get the class to call that method? I set a breakpoint inside the above method and it never goes in there.

To make the method called, you need to make the class (usually a ViewController) delegate of the UITextField.

class ViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var textField: UITextField!
    override func viewDidLoad() {
        super.viewDidLoad()
   
        self.textField.delegate = self
        //...
    }

    let TEXT_FIELD_LIMIT = 5
    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        //An expression works with Swift 2
        return (textField.text?.utf16.count ?? 0) + string.utf16.count - range.length <= TEXT_FIELD_LIMIT
        //For Swift 1.2
//        return count((textField.text ?? "").utf16) + count(string.utf16) - range.length <= TEXT_FIELD_LIMIT
    }

    //...
}


ADDITION:

The example above counts the Number of Characters based on UTF-16 representation, because range.length is given in UTF-16 base.

If you need to count the number of characters in other ways, the expression may get longer.

Ok I added in the references to UITextFieldDelegate and self.textField.delegate = self. I still can't get the method to be called.


In order to make this easier to understand the example code. How would you write the above code for a textbox called sampleTitle?

Have you dragged the text field (textbox) as an outlet in the view controller? You should have something like this:


class ViewController: UIViewController {
     @IBOutlet weak var sampleTitle: UITextField!

     override func viewDidLoad() {
          super.viewDidLoad()

          sampleTitle.delegate = self
     }
}

Yes, all of my text fields have an outlet in the view controller. I can't understand why this doesn't work.

I like to grab what the actual replacement will be and then look at that length, vs. trying to figure out what the new length would be via math. Feels more natual that way, and also allows for your "only valid characters" requirement. I use something like this in one of my projects to limit to just integer values.


    // Poorly named method on Apple's part.  This means integer values only.  It does not include decimal points
    private var nonDecimals = NSCharacterSet.decimalDigitCharacterSet().invertedSet

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        if string.rangeOfCharacterFromSet(nonDecimals, options: [], range: string.startIndex..<string.endIndex) != nil {
            return false
        }
       
        let start = advance(textField.text!.startIndex, range.location)
        let end = advance(start, range.length)
       
        let newString = textField.text!.stringByReplacingCharactersInRange(start..<end, withString: string)
       
        if newString.characters.count > 0 {

Sorry for getting a little bit off topic. But if you want to replace a part of String with UTF-16 based NSRange given, you need to write something like this:

        let start = advance(textField.text!.utf16.startIndex, range.location)
        let end = advance(start, range.length)
     
        let startIndex = String.Index(start, within: textField.text!)!
        let endIndex = String.Index(end, within: textField.text!)!
     
        let newString = textField.text!.stringByReplacingCharactersInRange(startIndex..<endIndex, withString: string)

Or, like this:

        let newString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)


If you removed all non-BMP characters, this wouldn't be needed, but NSCharacterSet.decimalDigitCharacterSet() actually contains some non-BMP characters.

It's really work! Thank you.

I just changed a little bit to accept the same number of emojis:


return (textField.text?.characters.count ?? 0) + string.characters.count - range.length < TEXT_FIELD_LIMIT

Whenever limiting text input, you also want to consider the user experience. Will people get frustrated because you aren't telling them why they can't type more?


You have a few options here


1.) Just don't let them type any extra characters. This isn't necessarily bad, especially if you're in time cruch and just need to ship the product.


2.) When they type extra characters, interrupt them with an alert. This can have its place if the type of data they've entered doesn't match what the app expects, and you need to explain why. For example a text input for number of minutes in the hour, and they enter "600" Well there aren't 600 minutes in an hour, so you might want to interrupt them and tell them to enter a number smaller than 60.


3.) Let them type the extra characters. When they go over the limit, show a red label with the number of characters they have gone overboard (A.K.A. Twitter sytle). This is my personal favorite because it's so much less intrusive. It gives people a visual so they can solve the problem in front of them. All they need to do is decrease the red number to zero. Then you validate their input once they try to save the data.


I have a framework out on github with a protocol you can use to get the third behavior. You might want to check it out.

https://github.com/TheoBendixson/CharacterLimited

Best Way to Limit Number of Characters in a Text Box?
 
 
Q