Set the maximum character length of a UITextField in Swift

IosStringSwiftUitextfieldCharacter

Ios Problem Overview


I know there are other topics on this, but I can't seem to find out how to implement it.

I'm trying to limit a UITextField to only five characters.

Preferably alphanumeric, -, ., and _.

I've seen this code:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange,
                       replacementString string: String) -> Bool
{
    let maxLength = 4
    let currentString: NSString = textField.text
    let newString: NSString =
             currentString.stringByReplacingCharactersInRange(range, withString: string)
    return newString.length <= maxLength
}

and

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

    let length = count(textField.text.utf16) + count(string.utf16) - range.length
    return length <= 10
}

How can I actually implement it? Which "textfield" should I swap out for my custom named UITextField?

Ios Solutions


Solution 1 - Ios

  1. Your view controller should conform to UITextFieldDelegate, like below:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    }
    
  2. Set the delegate of your textfield: myTextField.delegate = self

  3. Implement the method in your view controller:

    textField(_:shouldChangeCharactersInRange:replacementString:)
    

All together:

class MyViewController: UIViewController, UITextFieldDelegate  // Set delegate to class

@IBOutlet var mytextField: UITextField             //  textfield variable

override func viewDidLoad() {
    super.viewDidLoad()
    mytextField.delegate = self                  // set delegate
}


func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange,
                       replacementString string: String) -> Bool
{
    let maxLength = 4
    let currentString: NSString = textField.text
    let newString: NSString =
             currentString.stringByReplacingCharactersInRange(range, withString: string)
    return newString.length <= maxLength
}

For Swift 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let maxLength = 1
    let currentString: NSString = (textField.text ?? "") as NSString
    let newString: NSString =
        currentString.replacingCharacters(in: range, with: string) as NSString
    return newString.length <= maxLength
}

Allowing only a specified set of characters to be entered into a given text field

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

    if mytextField == numberField {
        if count(string) > 0 {
            let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789.-").invertedSet
            let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
            result = replacementStringIsLegal
        }
    }
    return result
}

How to program an iOS text field that takes only numeric input with a maximum length

Solution 2 - Ios

Modern Swift

Note that much of the example code online regarding this problem is extremely out of date.

Paste the following into any Swift file in your project. (You can name the file anything, for example, "Handy.swift".)

This finally fixes one of the stupidest problems in iOS:

enter image description here

Your text fields now have a .maxLength.

It is completely OK to set that value in storyboard during development, or, set it in code while the app is running.

// simply have this in any Swift file, say, Handy.swift

import UIKit
private var __maxLengths = [UITextField: Int]()
extension UITextField {
    @IBInspectable var maxLength: Int {
        get {
            guard let l = __maxLengths[self] else {
               return 150 // (global default-limit. or just, Int.max)
            }
            return l
        }
        set {
            __maxLengths[self] = newValue
            addTarget(self, action: #selector(fix), for: .editingChanged)
        }
    }
    func fix(textField: UITextField) {
        let t = textField.text
        textField.text = t?.prefix(maxLength).string
    }
}

It's that simple.


Footnote - these days to safely truncate a String in swift, you simply .prefix(n)


An even simpler one-off version...

The above fixes all text fields in your project.

If you just want one particular text field to simply be limited to say "4", and that's that...

class PinCodeEntry: UITextField {
    
    override func didMoveToSuperview() {
        
        super.didMoveToSuperview()
        addTarget(self, action: #selector(fixMe), for: .editingChanged)
    }
    
    @objc private func fixMe() { text = text?.prefix(4) }
}

Phew! That's all there is to it.

(Just BTW, here's a similar very useful tip relating to UITextView, https://stackoverflow.com/a/42333832/294884 )


For the OCD programmer (like me)...

As @LeoDabus reminds, .prefix returns a substring. If you're incredibly caring, this

let t = textField.text
textField.text = t?.prefix(maxLength)

would be

if let t: String = textField.text {
    textField.text = String(t.prefix(maxLength))
}

Enjoy!

Solution 3 - Ios

In Swift 4, simply use:

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    return range.location < 10
}

Solution 4 - Ios

The same way Steven Schmatz did it but using Swift 3.0 :

//max Length
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
               replacementString string: String) -> Bool
{
    let maxLength = 4
    let currentString: NSString = textField.text! as NSString
    let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
    return newString.length <= maxLength
}

Solution 5 - Ios

For Swift 5:

Just write one line to set the maximum character length:

 self.textField.maxLength = 10

For more details, see Max character limit of UITextField and allowed characters Swift. (Also credited.)

Solution 6 - Ios

I think an extension is more handy for this. See the full answer here.

private var maxLengths = [UITextField: Int]()

// 2
extension UITextField {

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

  func limitLength(textField: UITextField) {
    // 6
    guard let prospectiveText = textField.text
      where prospectiveText.characters.count > maxLength else {
        return
    }

    let selection = selectedTextRange
    // 7
    text = prospectiveText.substringWithRange(
      Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
    )
    selectedTextRange = selection
  }

}

Solution 7 - Ios

Simple solution without using a delegate:

TEXT_FIELD.addTarget(self, action: #selector(editingChanged(sender:)), for: .editingChanged)


@objc private func editingChanged(sender: UITextField) {

        if let text = sender.text, text.count >= MAX_LENGHT {
            sender.text = String(text.dropLast(text.count - MAX_LENGHT))
            return
        }
}

Solution 8 - Ios

My Swift 4 version of shouldChangeCharactersIn

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

    guard let preText = textField.text as NSString?,
        preText.replacingCharacters(in: range, with: string).count <= MAX_TEXT_LENGTH else {
        return false
    }

    return true
}

Solution 9 - Ios

Other solutions posted previously produce a retain cycle due to the textfield map. Besides, the maxLength property should be nullable if not set instead of artificial Int.max constructions; and the target will be set multiple times if maxLength is changed.

Here an updated solution for Swift4 with a weak map to prevent memory leaks and the other fixes

private var maxLengths = NSMapTable<UITextField, NSNumber>(keyOptions: NSPointerFunctions.Options.weakMemory, valueOptions: NSPointerFunctions.Options.strongMemory)

extension UITextField {

    var maxLength: Int? {
        get {
            return maxLengths.object(forKey: self)?.intValue
        }
        set {
            removeTarget(self, action: #selector(limitLength), for: .editingChanged)
            if let newValue = newValue {
                maxLengths.setObject(NSNumber(value: newValue), forKey: self)
                addTarget(self, action: #selector(limitLength), for: .editingChanged)
            } else {
                maxLengths.removeObject(forKey: self)
            }
        }
    }

    @IBInspectable var maxLengthInspectable: Int {
        get {
            return maxLength ?? Int.max
        }
        set {
            maxLength = newValue
        }
    }

    @objc private func limitLength(_ textField: UITextField) {
        guard let maxLength = maxLength, let prospectiveText = textField.text, prospectiveText.count > maxLength else {
            return
        }
        let selection = selectedTextRange
        text = String(prospectiveText[..<prospectiveText.index(from: maxLength)])
        selectedTextRange = selection
    }
}

Solution 10 - Ios

I give a supplementary answer based on @Frouo. I think his answer is the most beautiful way. Because it's a common control we can reuse. And there isn't any leak problem here.

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)
        }
    }

    // The method is used to cancel the check when using
    // the 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
    }


    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
    }

}

Solution 11 - Ios

Simply just check with the number of characters in the string

  1. Add a delegate to view controller and assign the delegate

    class YorsClassName : UITextFieldDelegate {
    
    }
    
  2. Check the number of characters allowed for the text field

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

Note: Here I checked for only characters allowed in textField.

Solution 12 - Ios

I have something to add to Alaeddine's answer:

  1. Your view controller should conform to UITextFieldDelegate

     class MyViewController: UIViewController, UITextViewDelegate {
    
     }
    
  2. Set the delegate of your textfield:

    To set the delegate, you can control drag from the textfield to your view controller in the storyboard. I think this is preferable to setting it in code

  3. Implement the method in your view controller:

     textField(_:shouldChangeCharactersInRange:replacementString:)
    

Solution 13 - Ios

Update for Fattie's answer:

extension UITextField {

    // Runtime key
    private struct AssociatedKeys {
        // Maximum length key
        static var maxlength: UInt8 = 0
        // Temporary string key
        static var tempString: UInt8 = 0
    }

    // Limit the maximum input length of the textfiled
    @IBInspectable var maxLength: Int {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.maxlength) as? Int ?? 0
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.maxlength, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            addTarget(self, action: #selector(handleEditingChanged(textField:)), for: .editingChanged)
        }
    }

    // Temporary string
    private var tempString: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.tempString) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.tempString, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    // When the text changes, process the amount of text in the input
    // box so that its length is within the controllable range.
    @objc private func handleEditingChanged(textField: UITextField) {

        // Special processing for the Chinese input method
        guard markedTextRange == nil else { return }

        if textField.text?.count == maxLength {

            // Set lastQualifiedString where text length == maximum length
            tempString = textField.text
        } else if textField.text?.count ?? 0 < maxLength {

            // Clear lastQualifiedString when text length > maxlength
            tempString = nil
        }

        // Keep the current text range in arcgives
        let archivesEditRange: UITextRange?

        if textField.text?.count ?? 0 > maxLength {

            // If text length > maximum length, remove last range and to move to -1 postion.
            let position = textField.position(from: safeTextPosition(selectedTextRange?.start), offset: -1) ?? textField.endOfDocument
            archivesEditRange = textField.textRange(from: safeTextPosition(position), to: safeTextPosition(position))
        } else {

            // Just set current select text range
            archivesEditRange = selectedTextRange
        }

        // Main handle string maximum length
        textField.text = tempString ?? String((textField.text ?? "").prefix(maxLength))

        // Last configuration edit text range
        textField.selectedTextRange = archivesEditRange
    }

    // Get safe textPosition
    private func safeTextPosition(_ optionlTextPosition: UITextPosition?) -> UITextPosition {

        /* beginningOfDocument -> The end of the the text document. */
        return optionlTextPosition ?? endOfDocument
    }
}

Solution 14 - Ios

TextField Limit Character After Block the Text in Swift 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: 
    NSRange,replacementString string: String) -> Bool
{
  
    
    if textField == self.txtDescription {
        let maxLength = 200
        let currentString: NSString = textField.text! as NSString
        let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
        return newString.length <= maxLength
    }
    
    return true
    
    
}

Solution 15 - Ios

  1. Set the delegate of your textfield:

     textField.delegate = self
    
  2. Implement the method in your view controller:

     // MARK: Text field delegate
    
     extension ViewController: UITextFieldDelegate {
         func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
             return range.location < maxLength (maxLength can be any maximum length you can define)
         }
     }
    

Solution 16 - Ios

Here's a Swift 3.2+ alternative that avoids unnecessary string manipulation. In this case, the maximum length is 10:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let text = textField.text ?? ""

    return text.count - range.length + string.count <= 10
}

Solution 17 - Ios

This answer is for Swift 4 and is pretty straightforward with the ability to let backspace through.

func textField(_ textField: UITextField,
               shouldChangeCharactersIn range: NSRange,
               replacementString string: String) -> Bool {
    return textField.text!.count < 10 || string == ""
}

Solution 18 - Ios

This is working In Swift 4

Step 1: Set UITextFieldDelegate
class SignUPViewController: UIViewController , UITextFieldDelegate {

   @IBOutlet weak var userMobileNoTextFiled: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
Step 2: Set the delegate
        userMobileNoTextFiled.delegate = self                  // Set delegate
   }

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    //        guard let text = userMobileNoTextFiled.text else { return true }
    //        let newLength = text.count + string.count - range.length
    //        return newLength <= 10
    //    }
Step 3: Call the function
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let maxLength = 10          // Set your need
        let currentString: NSString = textField.text! as NSString
        let newString: NSString =
            currentString.replacingCharacters(in: range, with: string) as NSString
        return newString.length <= maxLength
    }
}

Solution 19 - Ios

I use these steps. First set the delegate text field in viewdidload.

override func viewDidLoad() {
    super.viewDidLoad()

    textfield.delegate = self
}

And then shouldChangeCharactersIn after you include UITextFieldDelegate.

extension viewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let newLength = (textField.text?.utf16.count)! + string.utf16.count - range.length
        if newLength <= 8 {
            return true
        }
        else {
            return false
        }
    }
}

Solution 20 - Ios

Just in case, don't forget to guard the range size before applying it to the string. Otherwise, you will get a crash if the user will do this:

  • Type maximum length text
  • Insert something (nothing will be inserted due to the length limitation, but iOS doesn't know about it)
  • Undo insertion (you get a crash, because the range will be greater than the actual string size)

Also, using iOS 13 users can accidentally trigger this by gestures

I suggest you add to your project this

extension String {
    func replace(with text: String, in range: NSRange) -> String? {
        // NOTE: NSString conversion is necessary to operate in the same symbol steps
        // Otherwise, you may not be able to delete an emoji, for example
        let current = NSString(string: self)
        guard range.location + range.length <= current.length else { return nil }
        return current.replacingCharacters(in: range, with: text)
    }
}

And use it like this:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    guard let newText = textView.text.replace(with: text, in: range) else { return false }
    return newText.count < maxNumberOfCharacters
    // NOTE: You may wanna trim the new text instead,
    // so the user will able to shove his long text at least partially
}

Otherwise, you will constantly be getting crashed in your app.

Solution 21 - Ios

If you have multiple textField that have various length checks on one page I've found an easy and short solution.

class MultipleTextField: UIViewController {
    
    let MAX_LENGTH_TEXTFIELD_A = 10
    let MAX_LENGTH_TEXTFIELD_B = 11
    
    lazy var textFieldA: UITextField = {
        let textField = UITextField()
        textField.tag = MAX_LENGTH_TEXTFIELD_A
        textField.delegate = self
        return textField
    }()
    lazy var textFieldB: UITextField = {
        let textField = UITextField()
        textField.tag = MAX_LENGTH_TEXTFIELD_B
        textField.delegate = self
        return textField
    }()
}

extension MultipleTextField: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        return (range.location < textField.tag) && (string.count < textField.tag)
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionishkur88View Question on Stackoverflow
Solution 1 - IosAlaeddineView Answer on Stackoverflow
Solution 2 - IosFattieView Answer on Stackoverflow
Solution 3 - IosСергей БилыкView Answer on Stackoverflow
Solution 4 - IosPavlosView Answer on Stackoverflow
Solution 5 - IosAshish ChauhanView Answer on Stackoverflow
Solution 6 - IosZaEeM ZaFaRView Answer on Stackoverflow
Solution 7 - IosoberView Answer on Stackoverflow
Solution 8 - IosAviranView Answer on Stackoverflow
Solution 9 - IosAngel G. OlloquiView Answer on Stackoverflow
Solution 10 - IosVictor ChoyView Answer on Stackoverflow
Solution 11 - IosAkshay DigraseView Answer on Stackoverflow
Solution 12 - IosSwiftMattView Answer on Stackoverflow
Solution 13 - IoszhengView Answer on Stackoverflow
Solution 14 - IosPraveen ReddyView Answer on Stackoverflow
Solution 15 - IosJawad AliView Answer on Stackoverflow
Solution 16 - IosGreg BrownView Answer on Stackoverflow
Solution 17 - IosCodeBenderView Answer on Stackoverflow
Solution 18 - IosKeshav GeraView Answer on Stackoverflow
Solution 19 - IosJeri P.MView Answer on Stackoverflow
Solution 20 - IoskikiworaView Answer on Stackoverflow
Solution 21 - IosReza DehnaviView Answer on Stackoverflow