How can I get limit the user's TextField input to numbers in Swift?
You can use UITextFieldDelegate’s shouldChangeCharactersInRange method to limit the user's input to numbers:
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
// Create an `NSCharacterSet` set which includes everything *but* the digits
let inverseSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
// At every character in this "inverseSet" contained in the string,
// split the string up into components which exclude the characters
// in this inverse set
let components = string.componentsSeparatedByCharactersInSet(inverseSet)
// Rejoin these components
let filtered = components.joinWithSeparator("") // use join("", components) if you are using Swift 1.2
// If the original string is equal to the filtered string, i.e. if no
// inverse characters were present to be eliminated, the input is valid
// and the statement returns true; else it returns false
return string == filtered
}
Updated for Swift 3:
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
// Create an `NSCharacterSet` set which includes everything *but* the digits
let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted
// At every character in this "inverseSet" contained in the string,
// split the string up into components which exclude the characters
// in this inverse set
let components = string.components(separatedBy: inverseSet)
// Rejoin these components
let filtered = components.joined(separator: "") // use join("", components) if you are using Swift 1.2
// If the original string is equal to the filtered string, i.e. if no
// inverse characters were present to be eliminated, the input is valid
// and the statement returns true; else it returns false
return string == filtered
}
For anyone looking for a shorter answer, I've found this quite useful.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// remove non-numerics and compare with original string
return string == string.filter("0123456789".contains)
}
Works in XCode 10.1, Swift 4.2
In swift 4.1 and Xcode 10
Add UITextFieldDelegate to your class
class YourViewController: UIViewController, UITextFieldDelegate
Then write this code in your viewDidLoad()
yourTF.delegate = self
Write this textfield delegate function
//MARK - UITextField Delegates
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//For numers
if textField == yourTF {
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
}
1st you have to inherit the UITextViewDelegate class with you own
class
class ViewController: UIViewController, UITextViewDelegate {
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)
let Range = range.length + range.location > (fnameTF.text?.count)!
if Range == false && alphabet == false {
return false
}
let NewLength = (fnameTF.text?.count)! + string.count - range.length
return NewLength <= 10
} else {
return false
}
}
Well the iOS provides no such functionality where you can specify textfield to accept only numeric characters. The only way through would be, one of UITextFieldDelegate methods, which is as follows,
(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
You need to implement the following method and intercept the entered character and either through the following regular expression
"^([0-9]+)?(\\.([0-9]{1,2})?)?$"
or
[NSCharacterSet decimalDigitCharacterSet]
you can find out whether the entered character is numeric and return YES if it matches the regular expression or character set else return NO.
Related
I want to achieve following:
Have a decimal keypad. Which means user will be able to enter Double
values. (Needless to say "." will be limited one)
Prevent "0" characters as the first characters. (i.e.: There should
not be values like "003" "01" "000012" etc.)
Limit the character count to 10.
Only allow numbers. No Copy-Paste text values.
I am using decimal keypad. Below code handles first and third item above:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentString: NSString = (textField.text ?? "") as NSString
let newString = currentString.replacingCharacters(in: range, with: string)
return newString.count <= 10
}
Thank you for your time.
Bellow code will check all conditions you have specified
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//Prevent "0" characters as the first characters. (i.e.: There should not be values like "003" "01" "000012" etc.)
if textField.text?.count == 0 && string == "0" {
return false
}
//Limit the character count to 10.
if ((textField.text!) + string).count > 10 {
return false
}
//Have a decimal keypad. Which means user will be able to enter Double values. (Needless to say "." will be limited one)
if (textField.text?.contains("."))! && string == "." {
return false
}
//Only allow numbers. No Copy-Paste text values.
let allowedCharacterSet = CharacterSet.init(charactersIn: "0123456789.")
let textCharacterSet = CharacterSet.init(charactersIn: textField.text! + string)
if !allowedCharacterSet.isSuperset(of: textCharacterSet) {
return false
}
return true
}
You can use regex that define a number with <= 10 digits and not starting with 0, then use NSPredicate or NSRegularExpression to validate the entered text. Something like this:
func isAllowed(str: String?) -> Bool {
let regexPattern: String = "^((?!(0))[0-9]{0,10})$"
let predicate = NSPredicate(format:"SELF MATCHES %#", regexPattern)
return predicate.evaluate(with: str)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return isAllowed(str: textField.text)
}
You can Create this method to prevent from copy paste
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == "paste:" {
return false
}
return super.canPerformAction(action, withSender: sender)
}
and also you can add the following code to shouldChangeCharactersInRange Delegate method of textfield
let searchString = (txtMobilePhone.text as NSString?)?.replacingCharacters(in: range, with: string)
if (searchString?.length)! > 1 {
let inverseSet = CharacterSet(charactersIn: ".0123456789").inverted
return ((string as NSString).rangeOfCharacter(from: inverseSet).location == NSNotFound)
} else {
let inverseSet = CharacterSet(charactersIn: ".123456789").inverted
return ((string as NSString).rangeOfCharacter(from: inverseSet).location == NSNotFound)
}
this above will only allow the user to enter the "0" after the second character I mean restricts the user to type "0" at the starting of numbers
Am very new to swift language, Any help is appreciable, Here is my code
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField.text!.isEmpty {
textField.text = "0.00"
}
let range2 = string.rangeOfCharacterFromSet(NSMutableCharacterSet.decimalDigitCharacterSet())
if range2 != nil{
// textField.text = "M"
var f = textField.text?.floatValue
f=f!*10.0
f=f!+(string.floatValue/100.0)
textField.text = NSString(format: "%.2f", f!) as String
return false;
}
var f = textField.text?.floatValue
f=f!/10.0
textField.text = NSString(format: "%.2f", f!) as String
return false;
}
When am entering the numbers in texfield it's taking the numbers upto infinity. Please help me how to restrict the textfield to allow only 4 digits in my code.
First point, shouldChangeCharactersInRange is not a method for side effects. It should not be mutating any state, it should not change the value of the text field. It should merely return either true or false.
As I understand it, you want to disallow entry of more than four digits to the left of the decimal point.
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
let number = Float(newText)
return newText.isEmpty || newText == "." || number != nil && number! < 10000
}
}
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
}
Probably a simple one although no answers updated for Swift 3.
How can I append characters to a UITextField while the textfield is being edited? The characters should be appended while the user types not after the editing ends.
For example:
User types: 1
Field Updates: 1 kg
User types 123
Field Updates: 123 kg
Was trying to tackle this using EditingChanged IBActions but how can I stop the value from appending "kg" for each new character that is typed?
Example "1 kg 2 kg 3 kg"
Try this way it may help you out.
Add target for textfield on text is being editing
textField.addTarget(self, action: #selector(self.textFieldDidChange), for: .editingChanged)
In Observer method try the following way
func textFieldDidChange(textfield: UITextField) {
var text = textField.text?.replacingOccurrences(of: " KG", with: "")
text = text! + " KG"
textField.text = text
print("Text changed")
}
You want the UITextFieldDelegate method
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
I should warn you that this is incredibly irritating to implement because of the way Swift handles Strings. Its usually better to just cast the text to NSString (which is UTF16) and deal with the range that way. However if you are just doing numbers and can live with a fixed decimal place the case is much easier to handle. Keep a custom number that represents your "real number" and just update the field to reflect your formatted number. Since you only allow digits there are finitely many cases to handle (this code will not handle copy/paste).
You must set the textfield delegate and keyboard (to numeric) in the storyboard.
class ViewController: UIViewController{
#IBOutlet weak var textField: UITextField!
fileprivate let digits: Set<String> = ["0","1","2","3","4","5","6","7","8","9"]
fileprivate let decimalPlaces = 2
fileprivate let suffix = " kg"
fileprivate lazy var formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = self.decimalPlaces
formatter.maximumFractionDigits = self.decimalPlaces
formatter.locale = NSLocale.current
return formatter
}()
fileprivate var amount: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if digits.contains(string) {
amount *= 10
amount += Int(string)!
} else if string == "" {
amount /= 10
}
guard amount > 0 else {
textField.text = ""
return false
}
let digitsAfterDecimal = formatter.maximumFractionDigits
var value = Double(amount)
for _ in 0..<digitsAfterDecimal {
value /= 10
}
textField.text = formatter.string(from: NSNumber(value: value))! + suffix
return false
}
}
Conform the textfield delegate to the controller and try this solution.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string == "" {
// handle backspace scenario here
return true
} else if var textString = textField.text {
let unitString = "kg"
if textString.contains(unitString) {
textString = textString.replacingOccurrences(of: unitString, with: "")
textString += string + unitString
textField.text = textString
} else {
textField.text = string + unitString
}
return false
}
return true
}
Use UITextFieldDelegate's function:
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool // return NO to not change text
You'll have to combine the text from the textfield.text and string parameter from this function using the range parameter to get the string To Be formed on textfield while you type/paste/clear based on range.
Keep track on range.length as it always gives the count of string being deleted/cleared and is 0 when you enter text. And range.location gives the position of edit.
Then you can set the formed string to textfield.text and return false Remember - return false and not true
This code allows the users to type in certain characters only:
let allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.";
func textField(
textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool
{
let set = NSCharacterSet(charactersInString: allowedChars);
let filtered = string
.componentsSeparatedByCharactersInSet(set)
.joinWithSeparator("");
return filtered != string;
}
How can i make it so that it allows a just one textfield called "txtField1"?
You can check whether your textField is the one currently selected in your shouldChangeCharactersInRange method because as you can see in the parameters of this method you have access to the property textField which correspond to the current textField that you are selecting, so just check this at the beginning :
if texField == txtField1 {
// Do your stuff
} else {
// Do something else
}
Assign a value to the tag on your textfield that is unique to it (for example, 1).
txtField1.tag = 1
Then, update your method like this:
func textField(
textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool
{
if textField.tag == 1 {
let set = NSCharacterSet(charactersInString: allowedChars)
let filtered = string
.componentsSeparatedByCharactersInSet(set)
.joinWithSeparator("")
return filtered != string
}
return true
}
You can also omit the ; in Swift.