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.
Related
None of the solutions mentioned here:
How to input currency format on a text field (from right to left) using Swift?
work for me. Please don't mark this as duplicate.
I cannot subclass UITextField to give my own implementation.(This UITextField is also used elsewhere).
The only way that I see is to format replacementText string (user input string) in this method:
override func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentText = textField.text ?? ""
var replacementText = (currentText as NSString).replacingCharacters(in: range, with: string)
}
So when user enters a number it should show like this: 0.00.
When the user taps 1. We should have 0.01
When the user taps 2. We should display 0.12
When the user taps 3. We should display 1.23
When the user taps 4. We should display 12.34
private var enteredText: String = ""
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if enteredText.count > 0 && string == "" && range.length == 1 {
//Handles backspace
enteredText.removeLast()
}
else {
enteredText += string
}
return true
}
func textFieldDidChangeCharacter(textField:UITextField) {
if let number = NumberFormatter().number(from: enteredText) {
let double = number.doubleValue/100.0
let string = String(format: "%0.2f", double)
textField.text = string
}
}
In iOS 13, when implementing shouldChangeCharactersIn via the UITextfieldDelegate, the application crashes when using the swiping keyboard.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let text = textField.text as NSString? {
let txtAfterUpdate = text.replacingCharacters(in: range, with: string)
textField.text = txtAfterUpdate
}
return false
}
Is this an Apple bug?
I was able to reproduce this - if you mutate the state of the text on a UITextField during swipe entry - and only during swipe entry, it'll attempt to reinsert the swiped content (even if you return false), which retriggers your delegate event, which kicks off the recursive cycle.
It's a bit of a hack but you could catch it with something like
private var lastEntry: String?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.count > 1 && string == lastEntry { // implies we're swiping or pasting
print("Caught unwanted recursion")
return
}
lastEntry = string
if let text = textField.text as NSString? {
let txtAfterUpdate = text.replacingCharacters(in: range, with: string)
textField.text = txtAfterUpdate
}
return false
}
It'll stop users from pasting/swiping the same thing twice in a row, but at least it'll let them swipe while Apple fixes their problem.
I used UIPasteboard to identify when the user is pasting and then leave the text as the user entered using the swipe like this:
public func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
//check if the user used swipe keyboard
if string.count > 1 && string != UIPasteboard.general.string ?? "" {
return true
}
//do the text treatment
return false
}
I also realized that the TextField only accepts static strings when using swipe keyboard.
Hope it Helps.
Before setting text you can reset delegate and after set it to self again.
But this solution has one problem if textfield is empty - text will be doubled.
Му code example:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentText: String = textField.text ?? ""
if #available(iOS 13, *) {
textField.delegate = nil
let resultText = editedText
textField.text = resultText
if currentText.isEmpty, textField.text != resultText {
textField.text = resultText
}
textField.delegate = self
} else {
textField.text = input.result
}
return false
}
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 am attempting to regulate the input of a UITextfield in real time, meaning as a user is typing. I have this character set that i need to compare to the input string, and while editing if an unwarranted character is typed in, I want to relay an alert. Here is my character set :
let acceptedChars = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_")
now how do i capture a specific textfield in real time and track its input?
Try this:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let invalidCharacters = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_").invertedSet
if let range = string.rangeOfCharacterFromSet(invalidCharacters, options: nil, range:Range<String.Index>(start: string.startIndex, end: string.endIndex)) {
return false
}
return true
}
You can register your textField for value change event like this
textfield.addTarget(self, action:"textFieldDidChange", forControlEvents:UIControlEvents.EditingChanged)
func textFieldDidChange(){
// put your code
}
It will work for each chracter you have been typed in real time
var strings: NSString?
class ViewController: UIViewController,UITextFieldDelegate //set your textfield delegate
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{ if(textField .isEqual(your textfield))
{
strings=string;
let acceptedChars = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_").invertedSet;
if (strings!.rangeOfCharacterFromSet(acceptedChars.invertedSet).location != NSNotFound)
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
use below method
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if textField.isEqual(<textField whose value to be copied>)
{
<TextField to be updated>.text = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
}
return true
}
An Easy Delegate method and really more efficient is:
func textFieldDidChangeSelection(_ textField: UITextField) {
print(textField.text)
}
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.