482

How can I set the maximum amount of characters in a UITextField on the iPhone SDK when I load up a UIView?

JasonMArcher
  • 12,386
  • 20
  • 54
  • 51
Domness
  • 7,477
  • 8
  • 38
  • 49
  • this incredibly old answer is very out of date - the answer is now trivial: https://stackoverflow.com/a/38306929/294884 – Fattie Nov 13 '17 at 22:42
  • Possible duplicate of [Set the maximum character length of a UITextField in Swift](https://stackoverflow.com/questions/31363216/set-the-maximum-character-length-of-a-uitextfield-in-swift) – Fattie Nov 13 '17 at 22:42

45 Answers45

1044

While the UITextField class has no max length property, it's relatively simple to get this functionality by setting the text field's delegate and implementing the following delegate method:

Objective-C

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    // Prevent crashing undo bug – see note below.
    if(range.length + range.location > textField.text.length)
    {
        return NO;
    }
        
    NSUInteger newLength = [textField.text length] + [string length] - range.length;
    return newLength <= 25;
}

Swift

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
    let currentCharacterCount = textField.text?.count ?? 0
    if range.length + range.location > currentCharacterCount {
        return false
    }
    let newLength = currentCharacterCount + string.count - range.length
    return newLength <= 25
}

Before the text field changes, the UITextField asks the delegate if the specified text should be changed. The text field has not changed at this point, so we grab it's current length and the string length we're inserting (either through pasting copied text or typing a single character using the keyboard), minus the range length. If this value is too long (more than 25 characters in this example), return NO to prohibit the change.

When typing in a single character at the end of a text field, the range.location will be the current field's length, and range.length will be 0 because we're not replacing/deleting anything. Inserting into the middle of a text field just means a different range.location, and pasting multiple characters just means string has more than one character in it.

Deleting single characters or cutting multiple characters is specified by a range with a non-zero length, and an empty string. Replacement is just a range deletion with a non-empty string.

A note on the crashing "undo" bug

As is mentioned in the comments, there is a bug with UITextField that can lead to a crash.

If you paste in to the field, but the paste is prevented by your validation implementation, the paste operation is still recorded in the application's undo buffer. If you then fire an undo (by shaking the device and confirming an Undo), the UITextField will attempt to replace the string it thinks it pasted in to itself with an empty string. This will crash because it never actually pasted the string in to itself. It will try to replace a part of the string that doesn't exist.

Fortunately you can protect the UITextField from killing itself like this. You just need to ensure that the range it proposes to replace does exist within its current string. This is what the initial sanity check above does.

swift 3.0 with copy and paste working fine.

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        let str = (textView.text + text)
        if str.characters.count <= 10 {
            return true
        }
        textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
        return false
    }

Hope it's helpful to you.

Community
  • 1
  • 1
sickp
  • 14,642
  • 3
  • 25
  • 17
  • Works great... For both adding/deleting. – Chandan Shetty SP Feb 03 '11 at 14:09
  • How can I set this to just one UITextView on my screen? When I add it to my class it applies to all textviews. – Kyle Clegg Aug 06 '12 at 18:14
  • 7
    Found a solution by surrounding those 2 lines with if (textField == _ssn) { } and adding return YES; at the end of the method, which will allow all other UITextFields to accept text without any restriction. Nifty! – Kyle Clegg Aug 06 '12 at 23:48
  • Very nice answer. I would have liked to see one that accepts as much text as possible, instead of not allowing any (like if they try to add 10 chars, and have 9 left, add the first 9) -- like Windows edit controls work. But since I'm not willing to do the work myself, you get my vote good sir! :) (hopefully Apple adds this soon) – eselk Oct 15 '12 at 23:24
  • 55
    Good answer, but the ternary operator is superfluous; you could just put `return newLength <= 25;` – jowie Feb 26 '13 at 12:33
  • It was useful for me too. But, If I have 2 different textfield with differente size? ho can I do that? – doxsi Apr 16 '13 at 11:53
  • WOW thank you soo muchh!! This example sets the maximum length as 25 characters (25 inclusive) just for your reference – coolcool1994 Sep 04 '13 at 09:28
  • 2
    What if you just want the text to not spill over the frame of the textfield; not all characters are the same width, right ? – Morkrom Sep 30 '13 at 22:00
  • @Morkrom You're right, take a look on this long post about [NSString and Unicode](http://www.objc.io/issue-9/unicode.html) – Diogo T Mar 18 '14 at 20:08
  • For anyone having difficulty, make sure that the UITextField's delegate is set to self, and that the is set in the header file. – Scott Kohlert May 04 '14 at 13:00
  • Great example!! Thanks! @sickp – valbu17 Jul 10 '14 at 16:36
  • 2
    There really is an undo bug as @bcherry mentions – tested with `UITextField` on iPad iOS 8.1. (+1 to comment). Would the author add something about this as it's the top rated answer? I'm happy to do an edit, but mine regularly seem to get rejected! :-) – Benjohn Nov 24 '14 at 16:28
  • 1
    I've added a check for the crash and some notes about it drawn from here. – Benjohn Nov 24 '14 at 16:55
  • 1
    There is another bug in the system: autocorrection / prediction bypasses `-textField:shouldChangeCharactersInRange:replacementString` delegate method. I think that for completeness answer should mention this. – Jakub Vano Apr 14 '15 at 11:52
  • This method can't prevent user input text from apple's auto suggestion above the keyboard. – 6 1 Jun 27 '15 at 07:09
  • 10
    @bherry In Swift, the check for the undo case seems to break the entry of emoji. Shouldn't the count(textField.text) actually be count(textField.text.utf16) to match the objc/uikit reporting in range (vs Swift's variant of count on strings)? – rcw3 Jul 27 '15 at 17:28
  • 1
    In Swift 2.0, `count(textField.text)` needs to be `textField.text?.characters.count` – funroll Sep 17 '15 at 18:38
  • How to call this method in objc? – Danilo Pádua Feb 23 '16 at 11:39
  • 3
    For the Swift code comparing the textField.text?.characters.count with the range will add a bug when using Emoji characters. You should use the textField.text?.utf16.count for it to function as described. – Bogdan Onu Nov 22 '16 at 11:06
  • Is the bug still exist in latest iOS versions? I can't reproduce it on my iPhone with 10.2 and in simulator with 8.4. – jesse Jan 27 '17 at 14:17
  • Sorry, I rechecked, and yes, it still exists in both my cases. I wasn't able first reproduce it because if you comment out the 'bug-workaround code', it reveals another problem because newLength is unsigned, but the result may be negative. So negative values become very large positive numbers. And comparison returns NO, which hides the bug. – jesse Jan 27 '17 at 15:16
  • Swift 3: `func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {` – Jonny Mar 31 '17 at 09:55
  • This fails in swift when deleting the last character if it consists of multiple bytes (i.e. `range.length` > 1). Unfortunately `NSRange` and the `String` classes are not very compatible, so you can fall into issues like this. You need to convert to `NSString` objects. I will post the solution. – Guy Kogus Apr 07 '17 at 17:10
  • 1
    Guy, was the "Swift 3.0" solution above updated to handle the "deleting last character" issue? Also I noticed the "Swift 3.0" solution is for textView instead of textField? – Robert Smith Jun 28 '17 at 13:53
  • this is **ALMOST TEN YEARS OUT OF DATE**. heh! it is now trivial: https://stackoverflow.com/a/38306929/294884 – Fattie Nov 13 '17 at 22:43
  • 1
    Can you advise how to properly limit max text input length? i.e. if i limit input to 3 chars and try to input "ででで" which is achieved when i press keyboard buttons "dedede"(each "de" transforms into "で") then i am not able to input the last character, because my validation function considers that to be 4 characters. Here is my validation function: `-(BOOL)textField:(UITextField*)tf shouldChangeCharactersInRange:(NSRange)r replacementString:(NSString*)s {return [tf.text stringByReplacingCharactersInRange:r withString:s].length <= 3;}` This can be reproduced on Japanese - Romaji keyboard – igrek Nov 17 '17 at 17:22
  • the solution is cutting string length in text field did end editing, hence the author's solution does not handle this case for japanese-romaji keyboard – igrek Nov 21 '17 at 09:53
  • Suppose I have 2 textfields. Both require different max char limit, one require 10 char and other require 20 char. In that case what approach should I need to follow. Is there any way to make constant (25 in your code) to dynamic? – Sachin Rana Nov 28 '19 at 13:23
  • in order to always be able to remove text in case the text field already had text longer the length allowed I replaced ``` swift return newLength <= 25 ``` with ``` swift return newLength <= 25 || newLength < currentCharacterCount ``` that would fix a problem where a user wont be able to remove programatecally assigned text that exceeds the maxLenght – Mohamed Hashem Dec 03 '19 at 10:33
  • in my case when i use this code and the textField is at its max value, the primaryActionTriggered stops sending events.. so i always return true and do this hack if newLength > maxLength { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in guard let self = self else { return } self.text = self.text?.prefix(maxLength).description } } – Jean Raymond Daher Apr 11 '21 at 16:35
  • As others have mentioned, the Swift code prevents any new characters from being entered after an emoji character is added. Updating both `string.count` instances to `string.utf16.count` fixes this bug. – Ryan Maloney Apr 30 '21 at 18:22
70

Swift 4

import UIKit

private var kAssociationKeyMaxLength: Int = 0

extension UITextField {
    
    @IBInspectable var maxLength: Int {
        get {
            if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
                return length
            } else {
                return Int.max
            }
        }
        set {
            objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
            addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
        }
    }
    
    @objc func checkMaxLength(textField: UITextField) {
        guard let prospectiveText = self.text,
            prospectiveText.count > maxLength
            else {
                return
        }
        
        let selection = selectedTextRange
        
        let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
        let substring = prospectiveText[..<indexEndOfText]
        text = String(substring)
        
        selectedTextRange = selection
    }
}

Edit: memory leak issue fixed.

enter image description here

Community
  • 1
  • 1
frouo
  • 3,886
  • 1
  • 22
  • 26
56

Thank you august! (Post)

This is the code that I ended up with which works:

#define MAX_LENGTH 20

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if (textField.text.length >= MAX_LENGTH && range.length == 0)
    {
        return NO; // return NO to not change text
    }
    else
    {return YES;}
}
Community
  • 1
  • 1
Domness
  • 7,477
  • 8
  • 38
  • 49
  • 20
    Unfortunately, this solution fails to stop users copy-and-pasting into a text field, therefore allowing them to bypass the limit. Sickpea's answer copes with this situation correctly. – Ant Apr 17 '12 at 15:45
  • 5
    What is with people having an if statement to return NO or YES? Try this: return !(textField.text.length >= MAX_LENGTH && range.length == 0); – Matt Parkins Oct 08 '14 at 15:01
  • or this `return textField.text.length < MAX_LENGTH || range.length != 0;` – igrek Nov 21 '17 at 16:41
22

To complete August answer, an possible implementation of the proposed function (see UITextField's delegate).

I did not test domness code, but mine do not get stuck if the user reached the limit, and it is compatible with a new string that comes replace a smaller or equal one.

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    //limit the size :
    int limit = 20;
    return !([textField.text length]>limit && [string length] > range.length);
}
Jmini
  • 7,845
  • 2
  • 47
  • 66
17

You can't do this directly - UITextField has no maxLength attribute, but you can set the UITextField's delegate, then use:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
etolstoy
  • 1,788
  • 20
  • 33
August
  • 12,033
  • 3
  • 26
  • 30
  • 5
    I agree, this is the best way forward, but it stays a bit of a hack. Submit a bug report to Apple that you'd like to see a property for text length. I'm definitely interested in this as well. – avocade Jan 12 '09 at 00:12
  • 5
    @avocade, it's not a hack: it's an example where you have to do basic framework code that Apple should've done for you. There are MANY many examples of this in the iOS SDK. – Dan Rosenstark Nov 09 '11 at 10:30
14

Often you have multiple input fields with a different length.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    int allowedLength;
    switch(textField.tag) {
        case 1: 
            allowedLength = MAXLENGTHNAME;      // triggered for input fields with tag = 1
            break;
        case 2:
            allowedLength = MAXLENGTHADDRESS;   // triggered for input fields with tag = 2
            break;
        default:
            allowedLength = MAXLENGTHDEFAULT;   // length default when no tag (=0) value =255
            break;
    }

    if (textField.text.length >= allowedLength && range.length == 0) {
        return NO; // Change not allowed
    } else {
        return YES; // Change allowed
    }
}
Vincent
  • 4,076
  • 1
  • 36
  • 37
12

The best way would be to set up a notification on the text changing. In your -awakeFromNib of your view controller method you'll want:

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

Then in the same class add:

- (void)limitTextField:(NSNotification *)note {
    int limit = 20;
    if ([[myTextField stringValue] length] > limit) {
        [myTextField setStringValue:[[myTextField stringValue] substringToIndex:limit]];
    }
}

Then link up the outlet myTextField to your UITextField and it will not let you add any more characters after you hit the limit. Be sure to add this to your dealloc method:

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"UITextFieldTextDidChangeNotification" object:myTextField];
Paulo Moura
  • 17,501
  • 3
  • 20
  • 32
Martin Pilkington
  • 3,261
  • 20
  • 16
  • Although I did this to remove errors > myTextField.text=[myTextField.text substringToIndex:limit]; – Luke Nov 25 '14 at 05:34
11

This should be enough to solve the problem (replace 4 by the limit u want). Just make sure to add delegate in IB.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
     NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
     return (newString.length<=4);
}
EI Captain v2.0
  • 21,360
  • 10
  • 76
  • 103
Nishant
  • 11,895
  • 9
  • 53
  • 89
11

I created this UITextFieldLimit subclass:

  • Multiple textfields supported
  • Set the text length limit
  • Paste prevention
  • Displays a label of left characters inside the textfield, get hidden when you stop editing.
  • Shake animation when no characters left.

Grab the UITextFieldLimit.h and UITextFieldLimit.m from this GitHub repository:

https://github.com/JonathanGurebo/UITextFieldLimit

and begin to test!

Mark your storyboard-created UITextField and link it to my subclass using the Identity Inspector:

Identity Inspector

Then you can link it to an IBOutlet and set the limit(default is 10).


Your ViewController.h file should contain: (if you wan't to modify the setting, like the limit)

#import "UITextFieldLimit.h"

/.../

@property (weak, nonatomic) IBOutlet UITextFieldLimit *textFieldLimit; // <--Your IBOutlet

Your ViewController.m file should @synthesize textFieldLimit.


Set the text length limit in your ViewController.m file:

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

    [textFieldLimit setLimit:25];// <-- and you won't be able to put more than 25 characters in the TextField.
}

Hope the class helps you. Good luck!

Jonathan Gurebo
  • 1,049
  • 1
  • 11
  • 21
10

Use below extension to set the maximum character length of a UITextField and UITextView.

Swift 4.0

    private var kAssociationKeyMaxLength: Int = 0
    private var kAssociationKeyMaxLengthTextView: Int = 0
    extension UITextField {


        @IBInspectable var maxLength: Int {
            get {
                if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
                    return length
                } else {
                    return Int.max
                }
            }
            set {
                objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
                addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
            }
        }

        @objc func checkMaxLength(textField: UITextField) {
            guard let prospectiveText = self.text,
                prospectiveText.count > maxLength
                else {
                    return
            }

            let selection = selectedTextRange

            let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
            let substring = prospectiveText[..<indexEndOfText]
            text = String(substring)

            selectedTextRange = selection
        }
    }

UITextView

extension UITextView:UITextViewDelegate {


        @IBInspectable var maxLength: Int {
            get {
                if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLengthTextView) as? Int {
                    return length
                } else {
                    return Int.max
                }
            }
            set {
                self.delegate = self

                objc_setAssociatedObject(self, &kAssociationKeyMaxLengthTextView, newValue, .OBJC_ASSOCIATION_RETAIN)
            }
        }

        public func textViewDidChange(_ textView: UITextView) {
            checkMaxLength(textField: self)
        }
        @objc func checkMaxLength(textField: UITextView) {
            guard let prospectiveText = self.text,
                prospectiveText.count > maxLength
                else {
                    return
            }

            let selection = selectedTextRange

            let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
            let substring = prospectiveText[..<indexEndOfText]
            text = String(substring)

            selectedTextRange = selection
        }
    }

You can set limit below.

enter image description here

Hitesh Surani
  • 9,976
  • 5
  • 37
  • 55
  • best answer by far ,,, thanks a lot great solution when there is many textfields that need different lengths – Carlos Norena Nov 03 '18 at 14:30
  • Really great solution for textfield and textview control character input limit. Its save code lines and time too for developer ...:) – Sandip Patel - SM Jan 11 '19 at 07:02
  • This works better than the other solutions I found. For instance, if you have emojis in your textfield, other extensions I found would skip to the end of the line when editing. But your code doesn't do that. Thanks! – Phontaine Judd Dec 31 '19 at 05:25
7

Swift 3 version //***** This will NOT work with Swift 2.x! *****//

First create a new Swift file : TextFieldMaxLength.swift, and then add the code below:

import UIKit

private var maxLengths = [UITextField: Int]()

extension UITextField {

   @IBInspectable var maxLength: Int {

      get {

          guard let length = maxLengths[self] 
             else {
                return Int.max
      }
      return length
   }
   set {
      maxLengths[self] = newValue
      addTarget(
         self,
         action: #selector(limitLength),
         for: UIControlEvents.editingChanged
      )
   }
}
func limitLength(textField: UITextField) {
    guard let prospectiveText = textField.text,
        prospectiveText.characters.count > maxLength
    else {
        return
    }

   let selection = selectedTextRange
   let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
   text = prospectiveText.substring(to: maxCharIndex)
   selectedTextRange = selection
   }
}

and then you will see in Storyboard a new field (Max Length) when you select any TextField

if you still have more questions check out this link: http://www.globalnerdy.com/2016/05/18/ios-programming-trick-how-to-use-xcode-to-set-a-text-fields-maximum-length-visual-studio-style/

Gilad Brunfman
  • 3,146
  • 1
  • 27
  • 28
  • Warning, this global variable create memory leaks, by holding references to all the textviews. – frouo Mar 08 '17 at 15:44
7

I simulate the actual string replacement that's about to happen to calculate that future string's length:

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

    NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];

    if([newString length] > maxLength)
       return NO;

    return YES;
}
samvermette
  • 39,555
  • 25
  • 108
  • 143
6

The following code is similar to sickp's answer but handles correctly copy-paste operations. If you try to paste a text that is longer than the limit, the following code will truncate the text to fit the limit instead of refusing the paste operation completely.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    static const NSUInteger limit = 70; // we limit to 70 characters
    NSUInteger allowedLength = limit - [textField.text length] + range.length;
    if (string.length > allowedLength) {
        if (string.length > 1) {
            // get at least the part of the new string that fits
            NSString *limitedString = [string substringToIndex:allowedLength];
            NSMutableString *newString = [textField.text mutableCopy];
            [newString replaceCharactersInRange:range withString:limitedString];
            textField.text = newString;
        }
        return NO;
    } else {
        return YES;
    }
}
wroluk
  • 71
  • 1
  • 4
6

Using Interface builder you can link and get the event for "Editing changed" in any of your function. Now there you can put check for the length

- (IBAction)onValueChange:(id)sender 
{
    NSString *text = nil;
    int MAX_LENGTH = 20;
    switch ([sender tag] ) 
    {
        case 1: 
        {
            text = myEditField.text;
            if (MAX_LENGTH < [text length]) {
                myEditField.text = [text substringToIndex:MAX_LENGTH];
            }
        }
            break;
        default:
            break;
    }

}
Vishal Kumar
  • 61
  • 1
  • 1
6

There is generic solution for setting max length in Swift. By IBInspectable you can add new Attribute in Xcode Attribute Inspector.enter image description here

import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {

    @IBInspectable var maxLength: Int {
        get {
            guard let length = maxLengths[self]
            else {
                return Int.max
            }
            return length
        }
        set {
            maxLengths[self] = newValue
            addTarget(
                self,
                action: Selector("limitLength:"),
                forControlEvents: UIControlEvents.EditingChanged
            )
        }
    }

    func limitLength(textField: UITextField) {
        guard let prospectiveText = textField.text
            where prospectiveText.characters.count > maxLength else {
                return
        }
        let selection = selectedTextRange
        text = prospectiveText.substringWithRange(
            Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
        )
        selectedTextRange = selection
    }

}
Hiren
  • 649
  • 6
  • 12
  • Clean and perfect solution – Eduardo Jan 30 '17 at 11:38
  • Holding a UITextField in a global field will retain them in the memory even after the UIViewController is dismissed. This is a memory leak. Don't use this method. – Gunhan Feb 27 '19 at 09:38
5

To make it work with cut & paste of strings of any length, I would suggest changing the function to something like:

#define MAX_LENGTH 20

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    {
        NSInteger insertDelta = string.length - range.length;

        if (textField.text.length + insertDelta > MAX_LENGTH)
        {
           return NO; // the new string would be longer than MAX_LENGTH
        }
        else {
            return YES;
        }
    }
Påhl Melin
  • 657
  • 1
  • 7
  • 13
4

Swift 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard let text = textField.text else { return true }
    let newLength = text.count + string.count - range.length
    return newLength <= 10
}
Shan Ye
  • 2,372
  • 15
  • 21
3

Swift 2.0 +

First of all create a class for this process. Lets call it StringValidator.swift.

Then just paste the following code inside it.

import Foundation

extension String {

func containsCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) != nil
}

func containsOnlyCharactersIn(matchCharacters: String) -> Bool {
let disallowedCharacterSet = NSCharacterSet(charactersInString: matchCharacters).invertedSet
return self.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
}


func doesNotContainCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) == nil
}

func isNumeric() -> Bool
{
let scanner = NSScanner(string: self)
scanner.locale = NSLocale.currentLocale()

return scanner.scanDecimal(nil) && scanner.atEnd
}

}

Now save the class.....

Usage..

Now goto your viewController.swift class and make your textfield's outlets as..

@IBOutlet weak var contactEntryTxtFld: UITextField! //First textfield
@IBOutlet weak var contactEntryTxtFld2: UITextField!   //Second textfield

Now goto the textfield's shouldChangeCharactersInRange method and use like the following.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    if string.characters.count == 0 {
        return true
    }
    let latestText = textField.text ?? ""
    let checkAbleText = (latestText as NSString).stringByReplacingCharactersInRange(range, withString: string)


    switch textField {

    case contactEntryTxtFld:
        return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5

    case contactEntryTxtFld2:
        return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5

    default:
        return true
    }

}

Don't forget to set the delegate protocol/methods of textfields.

Let me explain about this... I am using the simple extension process of string which I wrote inside an another class. Now I am just calling those extension methods from another class where I need them by adding check and maximum value.

Features...

  1. It will set maximum limit of a particular textfield.
  2. It will set type of accepted keys for particular textfield.

Types...

containsOnlyCharactersIn //Accepts only Characters.

containsCharactersIn //Accepts combination of characters

doesNotContainsCharactersIn //Will not accept characters

Hope this helped.... Thanks..

onCompletion
  • 5,703
  • 4
  • 24
  • 34
3

swift 3.0

This code is working fine when you are paste string more than your character limits.

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        let str = (textView.text + text)
        if str.characters.count <= 10 {
            return true
        }
        textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
        return false
    }

Thanks for your votes. :)

Community
  • 1
  • 1
Ilesh P
  • 3,360
  • 1
  • 21
  • 43
3

I give a supplementary answer based on @Frouo. I think his answer is the most beautiful way. Becuase it's a common control we can reuse.

private var kAssociationKeyMaxLength: Int = 0

extension UITextField {

    @IBInspectable var maxLength: Int {
        get {
            if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
                return length
            } else {
                return Int.max
            }
        }
        set {
            objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
            self.addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
        }
    }

    func checkMaxLength(textField: UITextField) {

        guard !self.isInputMethod(), let prospectiveText = self.text,
            prospectiveText.count > maxLength
            else {
                return
        }

        let selection = selectedTextRange
        let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
        text = prospectiveText.substring(to: maxCharIndex)
        selectedTextRange = selection
    }

    //The method is used to cancel the check when use Chinese Pinyin input method.
    //Becuase the alphabet also appears in the textfield when inputting, we should cancel the check.
    func isInputMethod() -> Bool {
        if let positionRange = self.markedTextRange {
            if let _ = self.position(from: positionRange.start, offset: 0) {
                return true
            }
        }
        return false
    }

}
Victor Choy
  • 3,281
  • 21
  • 30
2

This is the correct way to handle max length on UITextField, it allows the return key to exit the resign the textfield as first responder and lets the user backspace when they reach the limit

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int MAX_LENGHT = 5;
    if([string isEqualToString:@"\n"])
    {
        [textField resignFirstResponder];
        return FALSE;
    }
    else if(textField.text.length > MAX_LENGHT-1)
    {
        if([string isEqualToString:@""] && range.length == 1)
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    else
    {
        return TRUE;
    }
}
anders
  • 3,945
  • 2
  • 21
  • 31
2

What about this simple approach. Its working fine for me.

extension UITextField {

    func  charactersLimit(to:Int) {

        if (self.text!.count > to) {
            self.deleteBackward()
        }
    }
}

Then:

someTextField.charactersLimit(to:16)
Joaquin Pereira
  • 207
  • 2
  • 14
1

Other answers do not handle the case where user can paste a long string from clipboard. If I paste a long string it should just be truncated but shown. Use this in your delegate:

static const NSUInteger maxNoOfCharacters = 5;

-(IBAction)textdidChange:(UITextField * )textField
{
NSString * text = textField.text;

if(text.length > maxNoOfCharacters)
{
    text = [text substringWithRange:NSMakeRange(0, maxNoOfCharacters)];
    textField.text = text;
}

// use 'text'

}
8suhas
  • 1,430
  • 10
  • 20
1

now how many characters u want just give values

 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range   replacementString:(NSString *)string {
     NSUInteger newLength = [textField.text length] + [string length] - range.length;
     return (newLength > 25) ? NO : YES;
  }
EI Captain v2.0
  • 21,360
  • 10
  • 76
  • 103
mahesh chowdary
  • 432
  • 5
  • 13
1

Got it down to 1 line of code :)

Set your text view's delegate to "self" then add the <UITextViewDelegate> in your .h and the following code in your .m .... you can adjust the number "7" to be whatever you want your MAXIMUM number of characters to be.

-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
    return ((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length)>0);
}

This code accounts for typing new characters, deleting characters, selecting characters then typing or deleting, selecting characters and cutting, pasting in general, and selecting characters and pasting.

Done!






Alternatively, another cool way to write this code with bit-operations would be

-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
    return 0^((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length));
}
Albert Renshaw
  • 15,644
  • 17
  • 92
  • 173
1

I have implemented a UITextField Extension to add a maxLength property to it.

It's based on Xcode 6 IBInspectables, so you can set the maxLength limit on the Interface builder.

Here is the implementation:

UITextField+MaxLength.h

#import <UIKit/UIKit.h>

@interface UITextField_MaxLength : UITextField<UITextFieldDelegate>

@property (nonatomic)IBInspectable int textMaxLength;
@end

UITextField+MaxLength.m

#import "UITextField+MaxLength.h"

@interface UITextField_MaxLength()

@property (nonatomic, assign) id <UITextFieldDelegate> superDelegate;

@end

@implementation UITextField_MaxLength

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

    //validate the length, only if it's set to a non zero value
    if (self.textMaxLength>0) {
        if(range.length + range.location > textField.text.length)
            return NO;

        if (textField.text.length+string.length - range.length>self.textMaxLength) {
            return NO;
        }
    }

    //if length validation was passed, query the super class to see if the delegate method is implemented there
    if (self.superDelegate && [self.superDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
        return [self.superDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
    }
    else{
        //if the super class does not implement the delegate method, simply return YES as the length validation was passed
        return YES;
    }
}

- (void)setDelegate:(id<UITextFieldDelegate>)delegate {
    if (delegate == self)
        return;
    self.superDelegate = delegate;
    [super setDelegate:self];
}

//forward all non overriden delegate methods
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([self.superDelegate  respondsToSelector:aSelector])
        return self.superDelegate;

    return [super forwardingTargetForSelector:aSelector];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    if ([self.superDelegate respondsToSelector:aSelector])
        return YES;

    return [super respondsToSelector:aSelector];
}
@end
Alex Nolasco
  • 17,012
  • 9
  • 71
  • 76
Lefteris
  • 14,002
  • 2
  • 52
  • 90
1

I have open sourced a UITextField subclass, STATextField, that offers this functionality (and much more) with its maxCharacterLength property.

Stunner
  • 11,108
  • 12
  • 79
  • 138
1

Use this code here RESTRICTED_LENGTH is length you want to restrict for textfield.

   - (BOOL)textField:(UITextField *)textField     shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if (textField == nameTF) {
    int limit = RESTRICTED_LENGTH - 1;
    return !([textField.text length]>limit && [string length] > range.length);
    }
   else
   {
    return YES;
   }

return NO;

}
Kendall Helmstetter Gelner
  • 73,251
  • 26
  • 123
  • 148
1

I did this in Swift for an 8 character limit when using a number pad.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    return !(textField.text?.characters.count == MAX_LENGTH && string != "")
}

I had to test for string != "" to allow the delete button to work on the number pad, otherwise it wouldn't allow deleting characters in the text field after it reached its max.

u84six
  • 3,515
  • 3
  • 28
  • 49
1

For Xamarin:

YourTextField.ShouldChangeCharacters = 
delegate(UITextField textField, NSRange range, string replacementString)
        {
            return (range.Location + replacementString.Length) <= 4; // MaxLength == 4
        };
Kedu
  • 1,310
  • 14
  • 25
1

If your purpose of limiting text count is to ensure that text will fit into a UILabel elsewhere, I'd avoid using character count. It breaks down with some emoji (trying to truncate a double size emoji will likely crash your app). It's also an issue with some languages like Japanese and Chinese, which have a two-step input process where a simple count just won't work.

I built out a UITextField drop-in subclass (MPC_CharacterLimitedTextField on github). You feed it the expected output label width and it will handle all languages, emoji, and pasting issues. It will harvest only as many full characters that will fit into the label, regardless of the character count. There's a demo in the project so you can test it to see if it's what you need. Hope it will help anybody who was having the same problems with output length that I was.

Mike Critchley
  • 1,595
  • 13
  • 20
1

You can also do this using NotificationCenter in Swift 4

NotificationCenter.default.addObserver(self, selector: #selector(self.handleTextChange(recognizer:)), name: NSNotification.Name.UITextFieldTextDidChange, object: yourTextField)

    @objc func handleTextChange(recognizer: NSNotification) {
            //max length is 50 charater max
            let textField = recognizer.object as! UITextField

            if((textField.text?.count)! > 50) {
                let newString: String? = (textField.text as NSString?)?.substring(to: 50)
                textField.text = newString

            }         
        }
Pramod More
  • 1,092
  • 2
  • 16
  • 47
1

Swift 4.2 and UITextFieldDelegate method

This works for me and limits the textfield to have a max input of 8 characters. Hopefully NSRange will eventually be changed to Range but for now I am happy to use NSString as creating a Range from NSRange involves dealing with another optional.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let text = textField.text ?? ""
    let nsString = text as NSString
    let newText = nsString.replacingCharacters(in: range, with: string)
    return newText.count <= 8
}
Edward
  • 2,304
  • 1
  • 19
  • 38
0

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

Jasmeet
  • 1,443
  • 1
  • 20
  • 37
0
(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];

    if ([txt_name.text length]>100)
    {
        return NO;
    }

    return YES;
}
Mubin Shaikh
  • 376
  • 4
  • 9
0

we can set the range of textfield like this..

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range  replacementString:(NSString *)string
{
     int setrange = 20;
     return !([textField.text length]>setrange && [string length] > range.length);
}
EI Captain v2.0
  • 21,360
  • 10
  • 76
  • 103
Rudra
  • 791
  • 7
  • 21
0

This limits the number of characters, but also make sure that you can paste in the field until the maximum limit.

- (void)textViewDidChange:(UITextView *)textView
{
    NSString* str = [textView text];
    str = [str substringToIndex:MIN(1000,[str length])];
    [textView setText:str];

    if([str length]==1000) {
        // show some label that you've reached the limit of 1000 characters
    }
}
jacklehamster
  • 311
  • 3
  • 6
0

Slightly beyond answering the original question, and expanding on Frouo's answer, here are extensions to trim a String of whitespace and to a max length, and to utilize those String extensions to trim a UITextField to a max length:

// In String_Extensions.swift

extension String {

  func trimmedString() -> String {
    var trimmedString = self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
    let components = trimmedString.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).filter { count($0) > 0 }
    return " ".join(components)
  }

  func trimmedStringToMaxLength(maxLength: Int) -> String {
    return trimmedString().substringToIndex(advance(startIndex, min(count(self), maxLength))).trimmedString()
  }

}

// In UITextField_Extensions.swift

private var maxLengthDictionary = [UITextField : Int]()
private var textFieldMaxLength = 20

extension UITextField {

  @IBInspectable var maxLength: Int {
    get {
      if let maxLength = maxLengthDictionary[self] {
        return maxLength
      } else {
        return textFieldMaxLength
      }
    }
    set {
      maxLengthDictionary[self] = newValue < textFieldMaxLength + 1 ? newValue : textFieldMaxLength
    }
  }

  func trimAndLimitToMaxLength() {
    text = text.trimmedStringToMaxLength(maxLength)
  }

}

let someTextField = UITextField()
let someString = "   This   is   a   string   that   is longer than allowable for a text field.   "
someTextField.text = someString
someTextField.trimAndLimitToMaxLength()
println(someTextField.text) // Prints "This is a string tha"
let anotherTextField = UITextField()
anotherTextField.maxLength = 5
anotherTextField.text = someString
anotherTextField.trimAndLimitToMaxLength()
println(anotherTextField.text) // Prints "This"

trimAndLimitToMaxLength() could be used in UITextFieldDelegate's textFieldDidEndEditing(_:) so that a user could enter or paste in a longer than acceptable string and then shorten it vs. just cutting off the input at the max length. In doing this, I would also set attributed text styles to indicate any portion of the text that goes beyond the acceptable length (e.g., [NSBackgroundColorAttributeName : UIColor.redColor(), NSForegroundColorAttributeName : UIColor.whiteColor(), NSStrikethroughStyleAttributeName : NSNumber(int: 1)]

Scott Gardner
  • 7,855
  • 1
  • 39
  • 34
  • Doesn't the global dictionary create memory leaks, by holding references to all the textviews (which reference the superviews up until root-) – Ixx Sep 06 '16 at 12:12
0

For Swift 2.1+

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

    if (range.length + range.location > textField.text!.characters.count )
    {
        return false;
    }

    let newLength = textField.text!.characters.count + string.characters.count - range.length
    return newLength <= 25
}

Hope it helps

ColossalChris
  • 5,078
  • 2
  • 34
  • 43
0

I want to add to the answer given by @sickp.

There is an issue in his Swift code that occurs with any multibyte text (e.g. emojis). NSRange and String in Swift are not compatible, so it's frustrating that the delegate class combines them. The trick is simply to convert the String objects to NSString The correct solution, based on what @sickp wrote, is actually this:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let currentText = (textField.text as NSString?) ?? NSString()
    let currentCharacterCount = currentText.length
    if range.length + range.location > currentCharacterCount {
        return false
    }
    let newLength = currentText.replacingCharacters(in: range, with: string).characters.count
    return newLength <= 25
}
Guy Kogus
  • 6,960
  • 1
  • 23
  • 28
0

for Swift 3.1 or later

firstly add protocol UITextFieldDelegate

like:-

class PinCodeViewController: UIViewController, UITextFieldDelegate { 
.....
.....
.....

}

after that create your UITextField and set delegate

Complete Exp: -

import UIKit

class PinCodeViewController: UIViewController, UITextFieldDelegate {

let pinCodetextField: UITextField = {
    let tf = UITextField()
    tf.placeholder = "please enter your pincode"
    tf.font = UIFont.systemFont(ofSize: 15)
    tf.borderStyle = UITextBorderStyle.roundedRect
    tf.autocorrectionType = UITextAutocorrectionType.no
    tf.keyboardType = UIKeyboardType.numberPad
    tf.clearButtonMode = UITextFieldViewMode.whileEditing;
    tf.contentVerticalAlignment = UIControlContentVerticalAlignment.center
 return tf
  }()


 override func viewDidLoad() {
   super.viewDidLoad()
   view.addSubview(pinCodetextField)
    //----- setup your textfield anchor or position where you want to show it----- 


    // after that 
pinCodetextField.delegate = self // setting the delegate

 }
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    return !(textField.text?.characters.count == 6 && string != "")
     } // this is return the maximum characters in textfield 
    }
kishu mewara
  • 2,434
  • 11
  • 27
0

Working in Swift 5.2:

 class AngListVC: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var angTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        angTextField.delegate = self
        angTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
      
    }

   func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
           let userText =  angTextField.text ?? ""
           var newText = ""
           if range.length > 0 {
               let txt = NSString(string: userText)
               if txt.length > 0 {
                   newText = txt.replacingCharacters(in: range, with: "")
               }
           } else {
               newText = userText + ""
           }
           return newText.count <= 3
       }

@objc func textFieldDidChange(_ textField: UITextField) {
        print("textFieldDidChange")
   }
-1
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if (textField.text.length >= 50) {
        return NO;
    }
    return YES;
}
Gaurav Gilani
  • 1,586
  • 14
  • 18
-1

Swift 4.2+

By implementing UITextFieldDelegate method

  1. ViewController:

    class MyViewController: UIViewController {
    
        let MAX_LENGTH = 256
    
        @IBOutlet weak var myTextField: UITextField!
    
        override viewDidLoad() {
            self.myTextField.delegate = self
        }
    }
    
  2. Delegate:

    extension MyViewController: UITextFieldDelegate {
    
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            let userText =  textView.text ?? ""
            var newText = ""
            if range.length > 0 {
                let txt = NSString(string: userText)
                if txt.length > 0 {
                    newText = txt.replacingCharacters(in: range, with: text)
                }
            } else {
                newText = userText + text
            }
            return newText.count <= MAX_LENGTH
        }
    
    }
    
Pocheshire
  • 114
  • 7
-6

I found this quick and simple

- (IBAction)backgroundClick:(id)sender {
    if (mytext.length <= 7) {
        [mytext resignFirstResponder];
    } else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Too Big" 
                                                        message:@"Please Shorten Name"
                                                       delegate:nil 
                                              cancelButtonTitle:@"Cancel"
                                              otherButtonTitles:nil];
        [alert show];
        [alert release];
    }
}
Simon Whitaker
  • 20,203
  • 4
  • 56
  • 79
  • Such usage of `UIAlertView`s is discouraged by Apple. You should reserve alerts for important messages, and just disallow input, as in accepted answer. – Dan Abramov Dec 28 '12 at 01:40
  • Not a proper way! --- The user will get to know about input size after inserting full string! – Hemang Jan 30 '13 at 06:28