Use UITextFieldDelegate for multiple fields - ios

I have the following code to prevent the user from entering more than one period . in a UITextField, it works fine but I would like to do the same with other two textFields, how can this be done?
I tried assigning delegate to multiple fields and it doesn't quite work, it only works on the first to be comes the first responded.
class UserInputViewController: UIViewController, UITextFieldDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.myTextField.delegate = self
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let periodsInTextField = myTextField.text!.componentsSeparatedByString(".").count - 1
if (periodsInTextField > 0 && string == "."){
return false
}
return true
}
}
EDIT(Answer) : Here is how it should be done based on # KKRocks' answer below.
class UserInputViewController: UIViewController, UITextFieldDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.myTextField.delegate = self
self.secondTextField.delegate = self
self.thirdTextField.delegate = self
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let periodsInTextField = textField.text!.componentsSeparatedByString(".").count - 1
if (periodsInTextField > 0 && string == "."){
return false
}
return true
}
}

Please change this line with
From :
let periodsInTextField = myTextField.text!.componentsSeparatedByString(".").count - 1
To :
let periodsInTextField = textField.text!.componentsSeparatedByString(".").count - 1

Related

textfield backspace action identify when textfield is empty?

I am trying to create otp textfield using five textfield.All working fine if you add top, but issue is occurred when user try to add textfield empty and trying to backspace and it was not call any delegate method of UItextfiled which I already added.
I tried this :-
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92) {
println("Backspace was pressed")
}
return true
}
but it's called when textfield is not empty.
For example :-
In below screen shot add 1 and on two different textfield and third one is empty but when I try to backspace it's need to go in second textfield(third is field is empty) this is what I was facing issue from mine side.
Thanks
followed by #Marmik Shah and #Prashant Tukadiya answer here I add my answer , for quick answer I taken the some code from here
step 1 :
create the IBOutletCollection for your all textfields as well as don't forget to set the tag in all textfields in the sequence order, for e.g [1,2,3,4,5,6]
class ViewController: UIViewController{
#IBOutlet var OTPTxtFields: [MyTextField]! // as well as set the tag for textfield in the sequence order
override func viewDidLoad() {
super.viewDidLoad()
//change button color and other options
OTPTxtFields.forEach { $0.textColor = .red; $0.backspaceTextFieldDelegate = self }
OTPTxtFields.first.becomeFirstResponder()
}
step 2 :
in your current page UITextField delegate method
extension ViewController : UITextFieldDelegate, MyTextFieldDelegate {
func textFieldDidEnterBackspace(_ textField: MyTextField) {
guard let index = OTPTxtFields.index(of: textField) else {
return
}
if index > 0 {
OTPTxtFields[index - 1].becomeFirstResponder()
} else {
view.endEditing(true)
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = ((textField.text)! as NSString).replacingCharacters(in: range, with: string)
if newString.count < 2 && !newString.isEmpty {
textFieldShouldReturnSingle(textField, newString : newString)
// return false
}
return newString.count < 2 || string == ""
//return true
}
override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)) || action == #selector(paste(_:)) {
return false
}
return true
}
func textFieldShouldReturnSingle(_ textField: UITextField, newString : String)
{
let nextTag: Int = textField.tag + 1
textField.text = newString
let nextResponder: UIResponder? = textField.superview?.viewWithTag(nextTag)
if let nextR = nextResponder
{
// Found next responder, so set it.
nextR.becomeFirstResponder()
}
else
{
// Not found, so remove keyboard.
textField.resignFirstResponder()
callOTPValidate()
}
}
}
Step 3:
create the textfield class for access the backward function
class MyTextField: UITextField {
weak var myTextFieldDelegate: MyTextFieldDelegate?
override func deleteBackward() {
if text?.isEmpty ?? false {
myTextFieldDelegate?.textFieldDidEnterBackspace(self)
}
super.deleteBackward()
}
}
protocol MyTextFieldDelegate: class {
func textFieldDidEnterBackspace(_ textField: MyTextField)
}
step - 4
finally follow the #Marmik Shah answer for custom class for your UITextField
Step 5
get the values from each textfield use this
func callOTPValidate(){
var texts: [String] = []
OTPTxtFields.forEach { texts.append($0.text!)}
sentOTPOption(currentText: texts.reduce("", +))
}
func sentOTPOption(currentText: String) {
print("AllTextfieldValue == \(currentText)")
}
You can override the function deleteBackward()
Create a new Class that inherits UITextField and trigger and EditingEnd event.
class MyTextField: UITextField {
override func deleteBackward() {
super.deleteBackward()
print("Backspace");
self.endEditing(true);
}
}
Then, in your Storyboard, add a custom class for the UITextField
Next, in your view controller, in the editingEnd action, you can switch the textfield. For this to work, you will need to set a tag value for each of your textfield.
For example, you have two text fields, tfOne and tfTwo.
tfOne.tag = 1; tfTwo.tag = 2
Now, if currently you are editing tfTwo and backspace is clicked, then you set the currently editing text field to tfOne
class ViewController: UIViewController {
#IBOutlet weak var tfOne: MyTextField!
#IBOutlet weak var tfTwo: MyTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func editingEnded(_ sender: UITextField) {
// UITextField editing ended
if(sender.tag == 2) {
self.tfOne.becomeFirstResponder();
}
}
}
You can give tag to your textfield in sequence like 101,102,103,104,105.
when backspace tapped. check the length of string is equal to 0. then goto textfield.tag - 1 until you get first textfield.like if you are on textfield 102 then goto textfield 102-1 = 101.
Same as when enter any character goto next textfield until you reach to last textfield like if you are on textfield 102 then goto textfield 102+1 = 103.
You can use (self.view.viewWithTag(yourTag) as? UITextField)?.becomeFirstResponder()
I don't have system with me so couldn't able to post code

Issue with limiting characters in textfield

This is how I am limiting the characters entered in two textFields...
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == textField1 {
if (textField1.text?.characters.count)! + (string.characters.count - range.length) > 11 {
return false
}
return true
} else if textField == textField2 {
if (textField2.text?.characters.count)! + (string.characters.count - range.length) > 15 {
return false
}
}
return true
}
But the issue is only textField1 is not allowing to enter more than 11 characters but textField2 is accepting any number of characters while it should not have allowed to enter more than 15 characters.
Since there's nothing wrong with your code, you can try following check list:
Check if textfield 2 outlet is set. You can do it manually or
you can try adding a breakpoint (or print) in you else if textField == textField2 {} block
Also check if delegate of textField2 is set to self as well
If control comes to else if block then your code must work.
I think you haven't set the delegate in viewDidLoad,
import UIKit
class ViewController: UIViewController, UITextFieldDelegate{
#IBOutlet var textField1 :UITextField!
#IBOutlet var textField2 :UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.textField1.delegate = self
self.textField2.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == textField1 {
if (textField1.text?.characters.count)! + (string.characters.count - range.length) > 11 {
return false
}
return true
} else if textField == textField2 {
if (textField2.text?.characters.count)! + (string.characters.count - range.length) > 11 {
return false
}
}
return true
}
Maybe you forgot to set textField2's delegate,And I did a test,your code is work!

How to call some method as soon as my UITextField length reaches 6 characters

I want to perform some action when my textField character count becomes 6 in swift.
Use the delegate
textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
and then implement
func textFieldDidChange(_ textField: UITextField) {
if textField.text?.characters.count == 6 {
// Call some method
}
}
You can use shouldChangeCharactersInRange to get length as well as position using range.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = (textField.text ?? "") as String
let txtAfterUpdate = text.replacingCharacters(in: range, with: string)
if (text.characters.count == 6 && range.location > 5) {
return false // if you want to restrict your string length of call some method here
}
return true
}

Change UITextField responder on Max Length

I have login controller where it has two textFields:
Access Card
Password
The Max Length for the access card is 9 and once the user type the ninth number, it should appear on the access card filed then the cursor needs to move to the password field.
In my code, the cursor is moving when the user clicks to enter the ninth number but the number doesn't appear and the cursor moves to the password field.
For example: I want to enter "123456789" as access card. Once I click "9" it doesn't appear but the cursor moves to password field:
LoginController.swift:
let ACCESSCARD_MAXLENGTH = 9
let PASSWORD_MAXLENGTH = 12
var AccessCardtextFieldLength = 0
var PasswordTextFieldLength = 0
class LoginViewController: UIViewController , UITextFieldDelegate {
#IBOutlet weak var AccessCardTextField: UITextField!
#IBOutlet weak var PasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// calling the function that initialize textFields
initializeTextFields()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// function is used to initialize textFields
func initializeTextFields () {
// To set the focus on the access card once the view load.
AccessCardTextField.becomeFirstResponder()
// This must be defined so we can apply the text field functions on it
AccessCardTextField.delegate = self
PasswordTextField.delegate = self
// Define the keyboard type of the textFields.
AccessCardTextField.keyboardType = UIKeyboardType.NumberPad
PasswordTextField.keyboardType = UIKeyboardType.ASCIICapable
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
AccessCardtextFieldLength = (textField.text?.characters.count)! + string.characters.count
PasswordTextFieldLength = (textField.text?.characters.count)! + string.characters.count
if (textField == AccessCardTextField){
for i in 0..<ACCESSCARD_MAXLENGTH{
if (AccessCardtextFieldLength == ACCESSCARD_MAXLENGTH){
PasswordTextField.becomeFirstResponder()
}
else{
return true
}
return false
}
}
if (textField == PasswordTextField){
return PasswordTextFieldLength <= PASSWORD_MAXLENGTH ? true : false
}
return true
}
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool will only update when it return true. In this case you are changing the firstResponder therefore it is not updated.
My suggestion is to use add target for this case. This is what you can do:
override func viewDidLoad() {
super.viewDidLoad()
// calling the function that initialize textFields
initializeTextFields()
accessCardTextField.addTarget(self, action: #selector(LoginViewController.accessCardTextFieldChanged(_:)), forControlEvents: .EditingChanged)
}
func accessCardTextFieldChanged(textField: UITextField) {
if textField.text?.characters.count == ACCESSCARD_MAXLENGTH {
modelTextField.becomeFirstResponder()
}
}
This way, it save you quite a few line of code. Most importantly, only accessCardTextField changed will be call. You could do another function to check your password textfield length separately. Also, i renamed from AccessCardTextField to accessCardTextField. It is recommended to have variable starting with lower case.
Not very sure if this works, but try edit your if statement contents with this.
if (AccessCardtextFieldLength == ACCESSCARD_MAXLENGTH){
if (textField == AccessCardTextField) {
textField.resignFirstResponder()
PasswordTextField.becomeFirstResponder()
}
}
In this condition
if (AccessCardtextFieldLength == ACCESSCARD_MAXLENGTH){
PasswordTextField.becomeFirstResponder()
**return true**
}
else{
return true
}
you returning flase that's why it doesn't show your last Character.

Limiting characters on UITextField breaks Emojis, can only add one and not delete

I'm using the following code to limit the amount of characters that can be in a UITextField
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.characters.count ?? 0
if (range.length + range.location > currentCharacterCount){
return false
}
let newLength = currentCharacterCount + string.characters.count - range.length
return newLength <= 44
}
This is done by setting the UITextField's delegate to self.
--
The issue here is that if you add an Emoji to the textfield, you cannot remove it. Even if you highlight - select all, or use "cut" the text will not change. The emoji can be before or after text, or even alone in the text field. You also cannot add two emojis to the field.
I don't see what the issue is here, can anyone help me out?
Its better if you don't do any checks inside shouldChange.. function and instead track the length separately using the UITextFieldTextDidChangeNotification notification. Sample code is attached below. In the shouldChange..just return a bool which is set in textDidChange. Here you will get the correct length. I noticed that in your method once you add an emoji it wont even allow you to type anymore. The shouldChangeCharactersInRange is not even getting called after entering a emoji char.
class ViewController: UIViewController, UITextFieldDelegate{
#IBOutlet weak var textField:UITextField!
var allow = true
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "textFieldDidChange:", name: UITextFieldTextDidChangeNotification, object: textField)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return allow
}
func textFieldDidChange(notification:NSNotification)
{
let length = textField.text?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
if length > 44 {
allow = false
} else {
allow = true
}
}
}
In Swift 3.1
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.characters.count ?? 0
let limit = 10
return currentCharacterCount < limit || string.characters.count < range.length
}
This won't prevent copy and paste long text string, I disabled the paste function for UITextField, with codes from
https://stackoverflow.com/a/39015132/6311644

Resources