When I've tried How to you set the maximum number of characters that can be entered into a UITextField using swift?, I saw that if I use all 10 characters, I can't erase the character too.
The only thing I can do is to cancel the operation (delete all the characters together).
Does anyone know how to not block the keyboard (so that I can't add other letters/symbols/numbers, but I can use the backspace)?
With Swift 5 and iOS 12, try the following implementation of textField(_:shouldChangeCharactersIn:replacementString:) method that is part of the UITextFieldDelegate protocol:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let textFieldText = textField.text,
let rangeOfTextToReplace = Range(range, in: textFieldText) else {
return false
}
let substringToReplace = textFieldText[rangeOfTextToReplace]
let count = textFieldText.count - substringToReplace.count + string.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 textField(_:shouldChangeCharactersIn:replacementString:) in a UIViewController:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var textField: UITextField! // Link this to a UITextField in Storyboard
override func viewDidLoad() {
super.viewDidLoad()
textField.smartInsertDeleteType = UITextSmartInsertDeleteType.no
textField.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let textFieldText = textField.text,
let rangeOfTextToReplace = Range(range, in: textFieldText) else {
return false
}
let substringToReplace = textFieldText[rangeOfTextToReplace]
let count = textFieldText.count - substringToReplace.count + string.count
return count <= 10
}
}
I do it like this:
func checkMaxLength(textField: UITextField!, maxLength: Int) {
if (countElements(textField.text!) > maxLength) {
textField.deleteBackward()
}
}
The code works for me. But I work with storyboard. In Storyboard I add an action for the text field in the view controller on editing changed.
Update for 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
}
you can extend UITextField and add an #IBInspectable object for handle it:
SWIFT 5
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)
}
}
#objc func fix(textField: UITextField) {
if let t = textField.text {
textField.text = String(t.prefix(maxLength))
}
}
}
and after that define it on attribute inspector
See Swift 4 original Answer
Add More detail from #Martin answer
// linked your button here
#IBAction func mobileTFChanged(sender: AnyObject) {
checkMaxLength(sender as! UITextField, maxLength: 10)
}
// linked your button here
#IBAction func citizenTFChanged(sender: AnyObject) {
checkMaxLength(sender as! UITextField, maxLength: 13)
}
func checkMaxLength(textField: UITextField!, maxLength: Int) {
// swift 1.0
//if (count(textField.text!) > maxLength) {
// textField.deleteBackward()
//}
// swift 2.0
if (textField.text!.characters.count > maxLength) {
textField.deleteBackward()
}
}
In Swift 4
10 Characters limit for text field and allow to delete(backspace)
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == userNameFTF{
let char = string.cString(using: String.Encoding.utf8)
let isBackSpace = strcmp(char, "\\b")
if isBackSpace == -92 {
return true
}
return textField.text!.count <= 9
}
return true
}
func checkMaxLength(textField: UITextField!, maxLength: Int) {
if (textField.text!.characters.count > maxLength) {
textField.deleteBackward()
}
}
a small change for IOS 9
Swift 3
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let nsString = NSString(string: textField.text!)
let newText = nsString.replacingCharacters(in: range, with: string)
return newText.characters.count <= limitCount
}
If you want to overwrite the last letter:
let maxLength = 10
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if range.location > maxLength - 1 {
textField.text?.removeLast()
}
return true
}
Swift 5
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == myTextFieldName {
if range.location > 10 {
return false
}
}
return true
}
or
func textFieldDidChangeSelection(_ textField: UITextField) {
myTextFieldName.text = String(myTextFieldName.text!.prefix(10))
}
Swift 5
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let MAX_LENGTH = 4
let updatedString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
return updatedString.count <= MAX_LENGTH
}
I posted a solution using IBInspectable, so you can change the max length value both in interface builder or programmatically. Check it out here
You can use in swift 5 or swift 4 like image look like bellow
Add textField in View Controller
Connect to text to ViewController
add the code in view ViewController
class ViewController: UIViewController , UITextFieldDelegate {
#IBOutlet weak var txtName: UITextField!
var maxLen:Int = 8;
override func viewDidLoad() {
super.viewDidLoad()
txtName.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if(textField == txtName){
let currentText = textField.text! + string
return currentText.count <= maxLen
}
return true;
}
}
You can download Full Source form GitHub: https://github.com/enamul95/TextFieldMaxLen
Since delegates are a 1-to-1 relationship and I might want to use it elsewhere for other reasons, I like to restrict textfield length adding this code within their setup:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setup()
}
required override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
func setup() {
// your setup...
setMaxLength()
}
let maxLength = 10
private func setMaxLength() {
addTarget(self, action: #selector(textfieldChanged(_:)), for: UIControlEvents.editingChanged)
}
#objc private func textfieldChanged(_ textField: UITextField) {
guard let text = text else { return }
let trimmed = text.characters.prefix(maxLength)
self.text = String(trimmed)
}
Beware of the undo bug for UITextField mentioned in this post: Set the maximum character length of a UITextField
here is how you fix it in swift
if(range.length + range.location > count(textField.text)) {
return false;
}
Here is my version of code. Hope it helps!
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let invalidCharacters = NSCharacterSet(charactersInString: "0123456789").invertedSet
if let range = string.rangeOfCharacterFromSet(invalidCharacters, options: nil, range:Range<String.Index>(start: string.startIndex, end: string.endIndex))
{
return false
}
if (count(textField.text) > 10 && range.length == 0)
{
self.view.makeToast(message: "Amount entry is limited to ten digits", duration: 0.5, position: HRToastPositionCenter)
return false
}
else
{
}
return true
}
I have been using this protocol / extension in one of my apps, and it's a little more readable. I like how it recognizes backspaces and explicitly tells you when a character is a backspace.
Some things to consider:
1.Whatever implements this protocol extension needs to specify a character limit. That's typically going to be your ViewController, but you could implement character limit as a computed property and return something else, for example a character limit on one of your models.
2. You will need to call this method inside of your text field's shouldChangeCharactersInRange delegate method. Otherwise you won't be able to block text entry by returning false, etc.
3. You will probably want to allow backspace characters through. That's why I added the extra function to detect backspaces. Your shouldChangeCharacters method can check for this and return 'true' early on so you always allow backspaces.
protocol TextEntryCharacterLimited{
var characterLimit:Int { get }
}
extension TextEntryCharacterLimited{
func charactersInTextField(textField:UITextField, willNotExceedCharacterLimitWithReplacementString string:String, range:NSRange) -> Bool{
let startingLength = textField.text?.characters.count ?? 0
let lengthToAdd = string.characters.count
let lengthToReplace = range.length
let newLength = startingLength + lengthToAdd - lengthToReplace
return newLength <= characterLimit
}
func stringIsBackspaceWith(string:String, inRange range:NSRange) -> Bool{
if range.length == 1 && string.characters.count == 0 { return true }
return false
}
}
If any of you are interested, I have a Github repo where I've taken some of this character limit behavior and put into an iOS framework. There's a protocol you can implement to get a Twitter-like character limit display that shows you how far you've gone above the character limit.
CharacterLimited Framework on Github
Im using this;
Limit 3 char
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let txt = textField.text {
let currentText = txt + string
if currentText.count > 3 {
return false
}
return true
}
return true
}
Here is my simple answer, using iOS 14+ and Xcode 12+ in Swift 5.0...
In viewDidLoad() add the following selector:
override func viewDidLoad() {
// Add a target for myTextField, pointing to .editingDidChange
myTextField.addTarget(self, action: #selector(myTextFieldDidChange(_:)), for: .editingChanged)
}
Somewhere in your class, you can also add an optional character limit:
// Add an optional character limit
let characterLimit = 100
Then later in your class, just add this function:
#objc func myTextFieldDidChange(_ textField: UITextField) {
textField.text = String(textField.text!.prefix(self.characterLimit))
}
This will limit your characters either as you type, OR when you copy+paste text into the text field.
You need to check whether the existing string plus the input is greater than 10.
func textField(textField: UITextField!,shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
NSUInteger newLength = textField.text.length + string.length - range.length;
return !(newLength > 10)
}
Related
I have a requirement to show "#" instead of bullets for password field.
But as there is no default option available for it in UITextField.
I have tried to write custom logic in "shouldChangeCharactersInRange"
But i am not able to handle the index when user will remove or add any specific character from in-between.
So here are my questions :-
1. Do i need to find any library
2. There is any other default option available for it?
3. Need to write custom logic for it? If so where i can handle it correctly "shouldChangeCharactersInRange" or "textFieldDidChange"
No you dont need to find any 3rd party library for this logic
No there is no default option available for your need
Yes, you need to write a custom logic for your demand, So here it goes...
var passwordText = String()
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == textFieldPassword {
var hashPassword = String()
let newChar = string.characters.first
let offsetToUpdate = passwordText.index(passwordText.startIndex, offsetBy: range.location)
if string == "" {
passwordText.remove(at: offsetToUpdate)
return true
}
else { passwordText.insert(newChar!, at: offsetToUpdate) }
for _ in passwordText.characters { hashPassword += "#" }
textField.text = hashPassword
return false
}
Swift 4:-
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == textFieldPassword {
var hashPassword = String()
let newChar = string.first
let offsetToUpdate = passwordText.index(passwordText.startIndex, offsetBy: range.location)
if string == "" {
passwordText.remove(at: offsetToUpdate)
return true
}
else { passwordText.insert(newChar!, at: offsetToUpdate) }
for _ in 0..<passwordText.count { hashPassword += "#" }
textField.text = hashPassword
return false
}
return true
}
Use a normal textfield without the secure input option. When a user enters a character, save it to a string variable, and replace it in the textfield with the character you wish to present instead of the bullets.
class ViewController: UIViewController,UITextFieldDelegate {
let textField = UITextField(frame :CGRect(x:16,y:50,width:200,height: 40))
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
self.view.addSubview(textField)
textField.becomeFirstResponder()
}
var password: String = ""
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool{
password = password+string
textField.text = textField.text!+"#"//Character you want
print("\(password)")
return false
}
}
This is in Swift 2. Hope it Helps!!
Improved Mr. Bean's answer in swift 5. To fix Copy&Paste bugs.
var passNSString : NSString = ""
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var hashPassword = String()
passNSString = passNSString.replacingCharacters(in: range, with: string) as NSString
for _ in 0..<passNSString.length { hashPassword += "#" }
textField.text = hashPassword
print("str", passNSString)
return false
}
I want the user to only enter numeric values in a UITextField. On iPhone we can show the numeric keyboard, but on iPad the user can switch to any keyboard.
Is there any way to restrict user to enter only numeric values in a UITextField?
Solution for swift 3.0 and above
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
Here is my 2 Cents. (Tested on Swift 2 Only)
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let aSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
let compSepByCharInSet = string.componentsSeparatedByCharactersInSet(aSet)
let numberFiltered = compSepByCharInSet.joinWithSeparator("")
return string == numberFiltered
}
This is just a little bit more strict. No decimal point either.
Hope it helps :)
PS: I assumed you looked after the delegate anyway.
Update: Swift 3.0 :
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
let compSepByCharInSet = string.components(separatedBy: aSet)
let numberFiltered = compSepByCharInSet.joined(separator: "")
return string == numberFiltered
}
In swift 4.1 and Xcode 9.4.1
Add UITextFieldDelegate to your class
class YourViewController: UIViewController, UITextFieldDelegate
Then write this code in your viewDidLoad()
mobileNoTF.delegate = self
Write this textfield delegate function
//MARK - UITextField Delegates
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//For mobile numer validation
if textField == mobileNoTF {
let allowedCharacters = CharacterSet(charactersIn:"+0123456789 ")//Here change this characters based on your requirement
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
return true
}
iPhone
In whatever UITextField you're getting these values from, you can specify the kind of keyboard you want to appear when somebody touches inside the text field.
E.G. a numeric-only keyboard.
Like this screenshot:
iPad
The iPad does not support the numeric keyboard, so your options are to either not support the iPad, validate the field post submit, or follow one of the other suggestions here to create same behaviors while running on an iPad.
Swift 2.0
For only allowing numbers and one "." decimal in uitextfield.
func textField(textField: UITextField,shouldChangeCharactersInRange range: NSRange,replacementString string: String) -> Bool
{
let newCharacters = NSCharacterSet(charactersInString: string)
let boolIsNumber = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newCharacters)
if boolIsNumber == true {
return true
} else {
if string == "." {
let countdots = textField.text!.componentsSeparatedByString(".").count - 1
if countdots == 0 {
return true
} else {
if countdots > 0 && string == "." {
return false
} else {
return true
}
}
} else {
return false
}
}
}
Accept decimal values in text fields with single (.)dot in Swift 3
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted
let components = string.components(separatedBy: inverseSet)
let filtered = components.joined(separator: "")
if filtered == string {
return true
} else {
if string == "." {
let countdots = textField.text!.components(separatedBy:".").count - 1
if countdots == 0 {
return true
}else{
if countdots > 0 && string == "." {
return false
} else {
return true
}
}
}else{
return false
}
}
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// return true if the replacementString only contains numeric characters
let digits = NSCharacterSet.decimalDigitCharacterSet()
for c in string {
if !digits.characterIsMember(c) {
return false
}
}
return true
}
This solution will work even if the user switches keyboards or tries to paste a non-numeric string into the text field.
Make sure to set the delegate property of the appropriate text field.
Extend your view controller like this:
class MyViewController: UIViewController, UITextFieldDelegate
In the viewDidLoad function extend to your text field like this:
myTextField.delegate = self
And then use the following function:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let isNumber = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
let withDecimal = (
string == NumberFormatter().decimalSeparator &&
textField.text?.contains(string) == false
)
return isNumber || withDecimal
}
This will now make sure the user can enter only decimal digits.
Swift 4 +
Accepts Number only
and accepts one separator
Use number formatter
Swift 4.x
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let s = NSString(string: textField.text ?? "").replacingCharacters(in: range, with: string)
guard !s.isEmpty else { return true }
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .none
return numberFormatter.number(from: s)?.intValue != nil
}
Here is a simple solution, you need to connect the event "Editing changed" to this method in your controller
Swift 4
#IBAction func valueChanged(_ sender: UITextField) {
if let last = sender.text?.last {
let zero: Character = "0"
let num: Int = Int(UnicodeScalar(String(last))!.value - UnicodeScalar(String(zero))!.value)
if (num < 0 || num > 9) {
//remove the last character as it is invalid
sender.text?.removeLast()
}
}
}
1st you have to inherit the UITextFieldDelegate class with you own
class
class ViewController: UIViewController, UITextFieldDelegate {
2nd add an IBOutlet
#IBOutlet weak var firstName: UITextField!
3rd you have to assure this object is using
override func viewDidLoad() {
super.viewDidLoad()
firstName.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == firstName {
let allowedCharacters = "1234567890"
let allowedCharacterSet = CharacterSet(charactersIn: allowedCharacters)
let typedCharacterSet = CharacterSet(charactersIn: string)
let alphabet = allowedCharacterSet.isSuperset(of: typedCharacterSet)
return alphabet
}
}
While most of these solutions will work, be aware that in some localisations a decimals are separated with a "," and not a "."
The cleaner way to do this would be
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let decimalCharacter = NSNumberFormatter().decimalSeparator
let characterSet = NSMutableCharacterSet.decimalDigitCharacterSet()
characterSet.addCharactersInString(decimalCharacter)
return replacementString.rangeOfCharacterFromSet(characterSet.invertedSet) == nil
}
Tested in swift 3.0
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
let numberOnly = NSCharacterSet.init(charactersIn: "0123456789")
let stringFromTextField = NSCharacterSet.init(charactersIn: string)
let strValid = numberOnly.isSuperset(of: stringFromTextField as CharacterSet)
return strValid
}
Here's an cleaner solution:
guard CharacterSet(charactersIn: "123456789").isSuperset(of: CharacterSet(charactersIn: string)) else {
return false
}
return true
For decimals just add ., example 123456789.
Set KeyboardType Property :- Number Pad
TextField Delegate please write below code
func textField(_ textField: UITextField, shouldChangeCharactersIn
range: NSRange, replacementString string: String) -> Bool {
if textField.text?.count == 0 && string == "0" {
return false
}
return string == string.filter("0123456789".contains)
}
Number should not start from 0 and entered number +ve.
//instead of these you can simply change your keyboard to number type
yourtextfield.keyboardType = UIKeyboardType.numberPad
I had actually done this when working through the Big Nerd Ranch book, my solution is:
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
let newCharacters = NSCharacterSet(charactersInString: string)
return NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newCharacters)
}
this only allows the numbers 0-9, to allow the "." as well is more complicated as you can only allow one "."
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let numRange = string.rangeOfCharacterFromSet(NSCharacterSet.letterCharacterSet()) {
return false
} else {
return true
}
}
To allow only numbers and just one decimal operator, you can use this solution:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let isNumber = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(NSCharacterSet(charactersInString: string))
return isNumber || (string == NSNumberFormatter().decimalSeparator && textField.text?.containsString(string) == false)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
let textString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if textField == self.phoneTextField && string.characters.count > 0{
let numberOnly = NSCharacterSet.decimalDigits
let strValid = numberOnly.contains(UnicodeScalar.init(string)!)
return strValid && textString.characters.count <= 10
}
return true
}
in above code is working in swift 3
NSCharacterSet.decimalDigits
You are also use letters only
NSCharacterSet.Letters
and uppercase,Lowercaseand,alphanumerics,whitespaces
is used same code
or See the Link
I think you can force change the keyboard type by implementing UITextInputTraits protocol, optional var keyboardType
//class ViewController: UIViewController, UITextInputTraits {
#IBOutlet weak var textFieldKeyboardType: UITextField!{
didSet{
textFieldKeyboardType.keyboardType = UIKeyboardType.NumberPad
}
}
var keyboardType: UIKeyboardType {
get{
return textFieldKeyboardType.keyboardType
}
set{
if newValue != UIKeyboardType.NumberPad{
self.keyboardType = UIKeyboardType.NumberPad
}
}
}
This is a more readable version that will do "0-9" plus ".":
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let existingTextHasDecimal = textField.text?.rangeOfString(".")
let replacementTextHasDecimal = string.rangeOfString(".")
let replacementTextAllCharacters = NSCharacterSet(charactersInString: string)
let replacementTextOnlyDigits = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(replacementTextAllCharacters)
if replacementTextHasDecimal != nil && existingTextHasDecimal != nil {
return false
}else{
if replacementTextOnlyDigits == true {
return true
}else if replacementTextHasDecimal != nil{
return true
}else{
return false
}
}
}
As if there aren't enough answers, here's mine. I think every example allowed for decimal separators is flawed in either localization, backspaces, or copy/paste.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty {return true} //allow for backspace
let decimalSeparator = NSNumberFormatter().decimalSeparator ?? "."
let validChars = NSMutableCharacterSet(charactersInString: decimalSeparator)
validChars.formUnionWithCharacterSet(NSCharacterSet.decimalDigitCharacterSet())
if validChars.isSupersetOfSet(NSCharacterSet(charactersInString: string)){
switch string.componentsSeparatedByString(decimalSeparator).count-1 {
case 0: //no decimals
return true
case 1: //if adding decimal, only allow if no existing decimal
if let existingText = textField.text{
return existingText.componentsSeparatedByString(decimalSeparator).count <= 1
}
else {return true}
default: //invalid decimals
return false
}
}
return false
}
func isValidNumber(str:String) -> Bool{
if str.isEmpty {
return false
}
let newChar = NSCharacterSet(charactersInString: str)
let boolValid = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newChar)
if boolValid{
return true
}else{
let lst = str.componentsSeparatedByString(".")
let newStr = lst.joinWithSeparator("")
let currentChar = NSCharacterSet(charactersInString: newStr)
if lst.count == 2 && !lst.contains("") && NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(currentChar){
return true
}
return false
}
}
Put this function in your "Submit" or "Save" method if there is one.
The following is the code I used in Swift 3.0 adapted from Mr H's code. Differences are because:
a) Delegate function declaration has changed in Swift 3.0. New declaration here
b) NSCharacterSet declaration has changed.
func textField(_ shouldChangeCharactersIntextField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted
let components = string.components(separatedBy: inverseSet)
let filtered = components.joined(separator: "")
return string == filtered
}
I have edited Raj Joshi's version to allow one dot or one comma:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let inverseSet = CharacterSet(charactersIn:"0123456789").inverted
let components = string.components(separatedBy: inverseSet)
let filtered = components.joined(separator: "")
if filtered == string {
return true
} else {
if string == "." || string == "," {
let countDots = textField.text!.components(separatedBy:".").count - 1
let countCommas = textField.text!.components(separatedBy:",").count - 1
if countDots == 0 && countCommas == 0 {
return true
} else {
return false
}
} else {
return false
}
}
}
You can use this code if you want to allow decimal separator and/or negative numbers.
But this code allows example: "34." (decimal separator at the end) while changing text. So you have to add some code example: textFieldShouldReturn or textFieldShouldEndEditing delegate functions.
The code written in Swift 4 but I assueme this is compatible with Swift 3.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else {
return true
}
let replaced = (text as NSString).replacingCharacters(in: range, with: string)
let decimalSeparator = NSLocale.current.decimalSeparator ?? ""
// When user wants to delete las character
if replaced == "" || replaced == "-" || replaced == "-0" {
textField.text = "0"
return false
}
// When text contains 0 before replace except "0."
if replaced != "0" + decimalSeparator && replaced.hasPrefix("0") && text.underestimatedCount == 1 {
textField.text = replaced.substring(from: replaced.index(after: replaced.startIndex))
return false
}
// When user wants to delete minus sign
if text.hasPrefix("-") && text.substring(from: text.index(after: text.startIndex)) == replaced {
return false
}
// When user wants to delete before decimal separator
if replaced.hasPrefix(decimalSeparator) || replaced.hasPrefix("-" + decimalSeparator) {
return false
}
// When user wants to add zero the beginning of number... but allowing "0." or "-0." numbers
let testReplaced = replaced.hasPrefix("-") ? replaced.substring(from: replaced.index(after: replaced.startIndex)) : replaced
if testReplaced.count >= 2 && testReplaced.hasPrefix("0") && !testReplaced.hasPrefix("0" + decimalSeparator) {
return false
}
// Every other cases
let allowDecimal = self.allowFloat ? (decimalSeparator == "." ? "\\.?" : decimalSeparator + "?") : ""
let allowSign = self.allowSigned ? "-?" : ""
let pattern = "\(allowSign)[0-9]+\(allowDecimal)([0-9]+)?"
do {
let regexRange = (replaced as NSString).range(of: replaced)
let regex = try NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matches(in: replaced, options: [], range: regexRange)
return matches.count == 1 && matches.first!.range == regexRange
}
catch {}
return false
}
If you don't want to allow decimal or negative numbers you have to replace tow variable with next line
let allowDecimal = ""
let allowSign = ""
For allow some charactors
func CheckAddress(string:String) -> Bool {
let numberOnly = NSCharacterSet.init(charactersIn: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#,&#/")
let stringFromTextField = NSCharacterSet.init(charactersIn: string)
return numberOnly.isSuperset(of: stringFromTextField as CharacterSet)
}
print("\(CheckAddress(string: "123"))") //True
print("\(CheckAddress(string: "asdf-"))") //True
print("\(CheckAddress(string: "asd123$"))") //false
The following solution has two benefits:
It is a one line code
It restricts the input so that the overall text in the input field is a valid number. Other solutions restricts the digits to valid numbers but this results in the user is able to enter "4...5"
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return NumberFormatter().numberFrom(text: (textField.text ?? "") + string) != nil
}
Swift 2.0
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
let inverseSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
let components = string.componentsSeparatedByCharactersInSet(inverseSet)
let filtered = components.joinWithSeparator("")
return string == filtered
}
I would like to force lowercase in an UITextfield when the user is typing.
I came out so far with this code, but seems like it's not lowering characters.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.characters.count == 0 {
return true
}
let currentText = textField.text ?? ""
let prospectiveText = (currentText as NSString).stringByReplacingCharactersInRange(range, withString: string.lowercaseString)
switch textField {
// Allow only lower-case vowels in this field,
// and limit its contents to a maximum of 6 characters.
case userNameTextField:
return prospectiveText.characters.count <= 27
default:
return true
}
}
First you should set following property on your textfield to restrict auto capitalisation:
textfield.autocapitalizationType = UITextAutocapitalizationType.None
And this is how you can restrict it further:
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
if let _ = string.rangeOfCharacterFromSet(NSCharacterSet.uppercaseLetterCharacterSet()) {
// Do not allow upper case letters
return false
}
return true
}
UPDATED FOR SWIFT 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let _ = string.rangeOfCharacter(from: .uppercaseLetters) {
// Do not allow upper case letters
return false
}
return true
}
You could do like this and lowercase the entire string when something has changed.
textfield.addTarget(self, action: "textViewChanged", forControlEvents: .EditingChanged);
func textViewChanged(){
textfield.text = textfield.text?.lowercaseString;
}
for swift 3 users, the above code given by Abhinav is just converted to the following
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let _ = string.rangeOfCharacter(from:NSCharacterSet.uppercaseLetters) {
return false
}
return true
}
if you want to convert all input characters to lower case you should do this code:
Swift 4:
in override func viewDidLoad() add this:
textfield.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControlEvents.editingChanged)
and then add this function to your class:
#objc func textFieldDidChange(_ textField: UITextField) {
if let text:String = textfield.text {
DispatchQueue.main.async {
self.textfield.text = text.lowercased()
}
}
}
it is necessary to change it in main thread.
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?
Your view controller should conform to UITextFieldDelegate, like below:
class MyViewController: UIViewController, UITextFieldDelegate {
}
Set the delegate of your textfield: myTextField.delegate = self
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
}
For Swift 5
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let maxLength = 1
let currentString = (textField.text ?? "") as NSString
let newString = currentString.replacingCharacters(in: range, with: string)
return newString.count <= 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 == textField {
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
Modern Swift
Note that a lot of the example code online is extremely out of date.
Paste the following into any Swift file in your project, example "Handy.swift".
This fixes one of the silliest problems in iOS:
Your text fields now have a .maxLength.
It is completely OK to set that value in storyboard or set in code while the app is running.
// 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.
An even simpler one-off version...
The above fixes all text fields in the whole 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) }
}
That's all there is to it.
(Here's a similar very useful tip relating to UITextView,
https://stackoverflow.com/a/42333832/294884 )
In Swift 4, simply use:
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return range.location < 10
}
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
}
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
}
}
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.)
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
}
}
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
}
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
}
}
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
}
}
Simply just check with the number of characters in the string
Add a delegate to view controller and assign the delegate
class YorsClassName : UITextFieldDelegate {
}
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.
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
}
I have something to add to Alaeddine's answer:
Your view controller should conform to UITextFieldDelegate
class MyViewController: UIViewController, UITextViewDelegate {
}
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
Implement the method in your view controller:
textField(_:shouldChangeCharactersInRange:replacementString:)
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
}
}
Set the delegate of your textfield:
textField.delegate = self
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)
}
}
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
}
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 == ""
}
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
}
}
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
}
}
}
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.
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)
}
}
lazy var textField: UITextField = {
let textField = UITextField()
textField.addTarget(self, #selector(handleOnEditing), for .editingChanged)
return textField
}()
//Set Delegate in ViewDidLoad
textField.delegate = self
#objc func handleOnEditing() {
let text = textField.text ?? ""
let limit = 10
textField.text = String(text.prefix(limit))
}
I want the user to only enter numeric values in a UITextField. On iPhone we can show the numeric keyboard, but on iPad the user can switch to any keyboard.
Is there any way to restrict user to enter only numeric values in a UITextField?
Solution for swift 3.0 and above
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
Here is my 2 Cents. (Tested on Swift 2 Only)
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let aSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
let compSepByCharInSet = string.componentsSeparatedByCharactersInSet(aSet)
let numberFiltered = compSepByCharInSet.joinWithSeparator("")
return string == numberFiltered
}
This is just a little bit more strict. No decimal point either.
Hope it helps :)
PS: I assumed you looked after the delegate anyway.
Update: Swift 3.0 :
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
let compSepByCharInSet = string.components(separatedBy: aSet)
let numberFiltered = compSepByCharInSet.joined(separator: "")
return string == numberFiltered
}
In swift 4.1 and Xcode 9.4.1
Add UITextFieldDelegate to your class
class YourViewController: UIViewController, UITextFieldDelegate
Then write this code in your viewDidLoad()
mobileNoTF.delegate = self
Write this textfield delegate function
//MARK - UITextField Delegates
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//For mobile numer validation
if textField == mobileNoTF {
let allowedCharacters = CharacterSet(charactersIn:"+0123456789 ")//Here change this characters based on your requirement
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
return true
}
iPhone
In whatever UITextField you're getting these values from, you can specify the kind of keyboard you want to appear when somebody touches inside the text field.
E.G. a numeric-only keyboard.
Like this screenshot:
iPad
The iPad does not support the numeric keyboard, so your options are to either not support the iPad, validate the field post submit, or follow one of the other suggestions here to create same behaviors while running on an iPad.
Swift 2.0
For only allowing numbers and one "." decimal in uitextfield.
func textField(textField: UITextField,shouldChangeCharactersInRange range: NSRange,replacementString string: String) -> Bool
{
let newCharacters = NSCharacterSet(charactersInString: string)
let boolIsNumber = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newCharacters)
if boolIsNumber == true {
return true
} else {
if string == "." {
let countdots = textField.text!.componentsSeparatedByString(".").count - 1
if countdots == 0 {
return true
} else {
if countdots > 0 && string == "." {
return false
} else {
return true
}
}
} else {
return false
}
}
}
Accept decimal values in text fields with single (.)dot in Swift 3
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted
let components = string.components(separatedBy: inverseSet)
let filtered = components.joined(separator: "")
if filtered == string {
return true
} else {
if string == "." {
let countdots = textField.text!.components(separatedBy:".").count - 1
if countdots == 0 {
return true
}else{
if countdots > 0 && string == "." {
return false
} else {
return true
}
}
}else{
return false
}
}
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// return true if the replacementString only contains numeric characters
let digits = NSCharacterSet.decimalDigitCharacterSet()
for c in string {
if !digits.characterIsMember(c) {
return false
}
}
return true
}
This solution will work even if the user switches keyboards or tries to paste a non-numeric string into the text field.
Make sure to set the delegate property of the appropriate text field.
Extend your view controller like this:
class MyViewController: UIViewController, UITextFieldDelegate
In the viewDidLoad function extend to your text field like this:
myTextField.delegate = self
And then use the following function:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let isNumber = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
let withDecimal = (
string == NumberFormatter().decimalSeparator &&
textField.text?.contains(string) == false
)
return isNumber || withDecimal
}
This will now make sure the user can enter only decimal digits.
Swift 4 +
Accepts Number only
and accepts one separator
Use number formatter
Swift 4.x
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let s = NSString(string: textField.text ?? "").replacingCharacters(in: range, with: string)
guard !s.isEmpty else { return true }
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .none
return numberFormatter.number(from: s)?.intValue != nil
}
Here is a simple solution, you need to connect the event "Editing changed" to this method in your controller
Swift 4
#IBAction func valueChanged(_ sender: UITextField) {
if let last = sender.text?.last {
let zero: Character = "0"
let num: Int = Int(UnicodeScalar(String(last))!.value - UnicodeScalar(String(zero))!.value)
if (num < 0 || num > 9) {
//remove the last character as it is invalid
sender.text?.removeLast()
}
}
}
1st you have to inherit the UITextFieldDelegate class with you own
class
class ViewController: UIViewController, UITextFieldDelegate {
2nd add an IBOutlet
#IBOutlet weak var firstName: UITextField!
3rd you have to assure this object is using
override func viewDidLoad() {
super.viewDidLoad()
firstName.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == firstName {
let allowedCharacters = "1234567890"
let allowedCharacterSet = CharacterSet(charactersIn: allowedCharacters)
let typedCharacterSet = CharacterSet(charactersIn: string)
let alphabet = allowedCharacterSet.isSuperset(of: typedCharacterSet)
return alphabet
}
}
While most of these solutions will work, be aware that in some localisations a decimals are separated with a "," and not a "."
The cleaner way to do this would be
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let decimalCharacter = NSNumberFormatter().decimalSeparator
let characterSet = NSMutableCharacterSet.decimalDigitCharacterSet()
characterSet.addCharactersInString(decimalCharacter)
return replacementString.rangeOfCharacterFromSet(characterSet.invertedSet) == nil
}
Tested in swift 3.0
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
let numberOnly = NSCharacterSet.init(charactersIn: "0123456789")
let stringFromTextField = NSCharacterSet.init(charactersIn: string)
let strValid = numberOnly.isSuperset(of: stringFromTextField as CharacterSet)
return strValid
}
Here's an cleaner solution:
guard CharacterSet(charactersIn: "123456789").isSuperset(of: CharacterSet(charactersIn: string)) else {
return false
}
return true
For decimals just add ., example 123456789.
Set KeyboardType Property :- Number Pad
TextField Delegate please write below code
func textField(_ textField: UITextField, shouldChangeCharactersIn
range: NSRange, replacementString string: String) -> Bool {
if textField.text?.count == 0 && string == "0" {
return false
}
return string == string.filter("0123456789".contains)
}
Number should not start from 0 and entered number +ve.
//instead of these you can simply change your keyboard to number type
yourtextfield.keyboardType = UIKeyboardType.numberPad
I had actually done this when working through the Big Nerd Ranch book, my solution is:
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
let newCharacters = NSCharacterSet(charactersInString: string)
return NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newCharacters)
}
this only allows the numbers 0-9, to allow the "." as well is more complicated as you can only allow one "."
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let numRange = string.rangeOfCharacterFromSet(NSCharacterSet.letterCharacterSet()) {
return false
} else {
return true
}
}
To allow only numbers and just one decimal operator, you can use this solution:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let isNumber = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(NSCharacterSet(charactersInString: string))
return isNumber || (string == NSNumberFormatter().decimalSeparator && textField.text?.containsString(string) == false)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
let textString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if textField == self.phoneTextField && string.characters.count > 0{
let numberOnly = NSCharacterSet.decimalDigits
let strValid = numberOnly.contains(UnicodeScalar.init(string)!)
return strValid && textString.characters.count <= 10
}
return true
}
in above code is working in swift 3
NSCharacterSet.decimalDigits
You are also use letters only
NSCharacterSet.Letters
and uppercase,Lowercaseand,alphanumerics,whitespaces
is used same code
or See the Link
I think you can force change the keyboard type by implementing UITextInputTraits protocol, optional var keyboardType
//class ViewController: UIViewController, UITextInputTraits {
#IBOutlet weak var textFieldKeyboardType: UITextField!{
didSet{
textFieldKeyboardType.keyboardType = UIKeyboardType.NumberPad
}
}
var keyboardType: UIKeyboardType {
get{
return textFieldKeyboardType.keyboardType
}
set{
if newValue != UIKeyboardType.NumberPad{
self.keyboardType = UIKeyboardType.NumberPad
}
}
}
This is a more readable version that will do "0-9" plus ".":
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let existingTextHasDecimal = textField.text?.rangeOfString(".")
let replacementTextHasDecimal = string.rangeOfString(".")
let replacementTextAllCharacters = NSCharacterSet(charactersInString: string)
let replacementTextOnlyDigits = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(replacementTextAllCharacters)
if replacementTextHasDecimal != nil && existingTextHasDecimal != nil {
return false
}else{
if replacementTextOnlyDigits == true {
return true
}else if replacementTextHasDecimal != nil{
return true
}else{
return false
}
}
}
As if there aren't enough answers, here's mine. I think every example allowed for decimal separators is flawed in either localization, backspaces, or copy/paste.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty {return true} //allow for backspace
let decimalSeparator = NSNumberFormatter().decimalSeparator ?? "."
let validChars = NSMutableCharacterSet(charactersInString: decimalSeparator)
validChars.formUnionWithCharacterSet(NSCharacterSet.decimalDigitCharacterSet())
if validChars.isSupersetOfSet(NSCharacterSet(charactersInString: string)){
switch string.componentsSeparatedByString(decimalSeparator).count-1 {
case 0: //no decimals
return true
case 1: //if adding decimal, only allow if no existing decimal
if let existingText = textField.text{
return existingText.componentsSeparatedByString(decimalSeparator).count <= 1
}
else {return true}
default: //invalid decimals
return false
}
}
return false
}
func isValidNumber(str:String) -> Bool{
if str.isEmpty {
return false
}
let newChar = NSCharacterSet(charactersInString: str)
let boolValid = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newChar)
if boolValid{
return true
}else{
let lst = str.componentsSeparatedByString(".")
let newStr = lst.joinWithSeparator("")
let currentChar = NSCharacterSet(charactersInString: newStr)
if lst.count == 2 && !lst.contains("") && NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(currentChar){
return true
}
return false
}
}
Put this function in your "Submit" or "Save" method if there is one.
The following is the code I used in Swift 3.0 adapted from Mr H's code. Differences are because:
a) Delegate function declaration has changed in Swift 3.0. New declaration here
b) NSCharacterSet declaration has changed.
func textField(_ shouldChangeCharactersIntextField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted
let components = string.components(separatedBy: inverseSet)
let filtered = components.joined(separator: "")
return string == filtered
}
I have edited Raj Joshi's version to allow one dot or one comma:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let inverseSet = CharacterSet(charactersIn:"0123456789").inverted
let components = string.components(separatedBy: inverseSet)
let filtered = components.joined(separator: "")
if filtered == string {
return true
} else {
if string == "." || string == "," {
let countDots = textField.text!.components(separatedBy:".").count - 1
let countCommas = textField.text!.components(separatedBy:",").count - 1
if countDots == 0 && countCommas == 0 {
return true
} else {
return false
}
} else {
return false
}
}
}
You can use this code if you want to allow decimal separator and/or negative numbers.
But this code allows example: "34." (decimal separator at the end) while changing text. So you have to add some code example: textFieldShouldReturn or textFieldShouldEndEditing delegate functions.
The code written in Swift 4 but I assueme this is compatible with Swift 3.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else {
return true
}
let replaced = (text as NSString).replacingCharacters(in: range, with: string)
let decimalSeparator = NSLocale.current.decimalSeparator ?? ""
// When user wants to delete las character
if replaced == "" || replaced == "-" || replaced == "-0" {
textField.text = "0"
return false
}
// When text contains 0 before replace except "0."
if replaced != "0" + decimalSeparator && replaced.hasPrefix("0") && text.underestimatedCount == 1 {
textField.text = replaced.substring(from: replaced.index(after: replaced.startIndex))
return false
}
// When user wants to delete minus sign
if text.hasPrefix("-") && text.substring(from: text.index(after: text.startIndex)) == replaced {
return false
}
// When user wants to delete before decimal separator
if replaced.hasPrefix(decimalSeparator) || replaced.hasPrefix("-" + decimalSeparator) {
return false
}
// When user wants to add zero the beginning of number... but allowing "0." or "-0." numbers
let testReplaced = replaced.hasPrefix("-") ? replaced.substring(from: replaced.index(after: replaced.startIndex)) : replaced
if testReplaced.count >= 2 && testReplaced.hasPrefix("0") && !testReplaced.hasPrefix("0" + decimalSeparator) {
return false
}
// Every other cases
let allowDecimal = self.allowFloat ? (decimalSeparator == "." ? "\\.?" : decimalSeparator + "?") : ""
let allowSign = self.allowSigned ? "-?" : ""
let pattern = "\(allowSign)[0-9]+\(allowDecimal)([0-9]+)?"
do {
let regexRange = (replaced as NSString).range(of: replaced)
let regex = try NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matches(in: replaced, options: [], range: regexRange)
return matches.count == 1 && matches.first!.range == regexRange
}
catch {}
return false
}
If you don't want to allow decimal or negative numbers you have to replace tow variable with next line
let allowDecimal = ""
let allowSign = ""
For allow some charactors
func CheckAddress(string:String) -> Bool {
let numberOnly = NSCharacterSet.init(charactersIn: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#,&#/")
let stringFromTextField = NSCharacterSet.init(charactersIn: string)
return numberOnly.isSuperset(of: stringFromTextField as CharacterSet)
}
print("\(CheckAddress(string: "123"))") //True
print("\(CheckAddress(string: "asdf-"))") //True
print("\(CheckAddress(string: "asd123$"))") //false
The following solution has two benefits:
It is a one line code
It restricts the input so that the overall text in the input field is a valid number. Other solutions restricts the digits to valid numbers but this results in the user is able to enter "4...5"
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return NumberFormatter().numberFrom(text: (textField.text ?? "") + string) != nil
}
Swift 2.0
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
let inverseSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
let components = string.componentsSeparatedByCharactersInSet(inverseSet)
let filtered = components.joinWithSeparator("")
return string == filtered
}