Reputation: 13
What I want to implement is, TextField format should be XXXX XXXX XXXX when the user clicks on the show button it should show the text 1234 5678 9123
Below is my code where I was able to implement the 1234 5678 9123 format, masking and storing original data is pending.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
if textField.tag == 0 || textField.tag == 1 {
if string.isBackspace
{
if range.location == 5 || range.location == 10
{
textField.text?.removeLast()
}
}
if string == ""{
return true
}
//range.length will be greater than 0 if user is deleting text - allow it to replace
if range.length > 0
{
return true
}
//Don't allow empty strings
if string == " "
{
return false
}
//Check for max length including the spacers we added
if range.location > 13 //23
{
return false
}
var originalText = textField.text
let replacementText = string.replacingOccurrences(of: " ", with: "")
//Verify entered text is a numeric value
let digits = NSCharacterSet.decimalDigits
for char in replacementText.unicodeScalars
{
if !(digits as NSCharacterSet).longCharacterIsMember(char.value)
{
return false
}
}
//Put an empty space after every 4 places
if (originalText?.count)! > 0
{
if (originalText?.count)! < 5 && (originalText?.count)! % 4 == 0{
originalText?.append(" ")
}else if(((originalText?.count)! + 1) % 5 == 0){
originalText?.append(" ")
}
}
textField.text = originalText
}
//2 - Nick Name,3 - Recipient Name
if textField.tag == 2 || textField.tag == 3 {
//Max Limit for Nick Name and Benificiary Name is 65
if range.location > 64
{
return false
}
}
return true
}
Upvotes: 1
Views: 3041
Reputation: 13
Finally i am able to solve this, Here is the Solution by Adwait Barkale. Any Queries Mail me at [email protected]
NOTE:- arrPersonalDetails[2].value is just a String(use a variable where you want to store the original text entered)
1.UITextfield Delegate
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
if textField.tag == 0 || textField.tag == 1 {
//For Masking Numbers 1234 5678 9123 into XXXX XXXX XXXX
//1.Storing original data 1234 5678 9123 into arrPersonalDetails[index].value
//2.Formatting textfield by putting spaces after every 4 and 8 digit
//Step 1 Store Data Locally
if string.isBackspace
{
if range.location == 5 || range.location == 10
{
textField.text?.removeLast()
}
if arrPersonalDetails[textField.tag].value!.count == 11 || arrPersonalDetails[textField.tag].value!.count == 6
{
arrPersonalDetails[textField.tag].value!.removeLast()
}
}
//Check for max length including the spacers we added
if range.location > 13 //23
{
return false
}
if string.isBackspace
{
if arrPersonalDetails[textField.tag].value!.count != 0 && arrPersonalDetails[textField.tag].value!.count != 11 || arrPersonalDetails[textField.tag].value!.count != 0 && arrPersonalDetails[textField.tag].value!.count != 6
{
let truncated = String(arrPersonalDetails[textField.tag].value!.dropLast())
arrPersonalDetails[textField.tag].value = truncated
}
} else {
if arrPersonalDetails[textField.tag].value!.count == 4 || arrPersonalDetails[textField.tag].value!.count == 9
{
arrPersonalDetails[textField.tag].value!.append(" ")
}
arrPersonalDetails[textField.tag].value!.append(string)
}
let oValue = arrPersonalDetails[textField.tag].value
arrPersonalDetails[textField.tag].value = oValue
if string == ""{
return true
}
//range.length will be greater than 0 if user is deleting text - allow it to replace
if range.length > 0
{
return true
}
//Don't allow empty strings
if string == " "
{
return false
}
//Step 2 - Manage Textfield Data
var originalText = textField.text
let replacementText = string.replacingOccurrences(of: " ", with: "")
//Verify entered text is a numeric value
let digits = NSCharacterSet.decimalDigits
for char in replacementText.unicodeScalars
{
if !(digits as NSCharacterSet).longCharacterIsMember(char.value)
{
return false
}
}
//Put an empty space after every 4 places
if (originalText?.count)! > 0
{
if (originalText?.count)! < 5 && (originalText?.count)! % 4 == 0{
originalText?.append(" ")
}else if(((originalText?.count)! + 1) % 5 == 0){
originalText?.append(" ")
}
}
textField.text = originalText
}
return true
}
2.Add Target to Textfield
cell.tflData.addTarget(self, action: #selector(maskTextfield(tfl:)), for: .editingChanged)
/// Function to mask unmask textfiel's text
/// - Parameter tfl: UITextfield
@objc func maskTextfield(tfl: UITextField)
{
let value = arrPersonalDetails[tfl.tag].value
if showOriginalText //Change this value on your button click
{
tfl.text = value
} else {
//Masking numbers with X
var maskedText = ""
if value!.count != 0 {
for i in 1...value!.count {
if i == 5 || i == 10 {
maskedText.append(" ")
} else {
maskedText.append("X")
}
}
}
print("Masked Text = \(maskedText)")
tfl.text = maskedText
}
}
Edit:-
Note :- In Textfield did end editing don't write someVariable = textField.text other wise it will take XXXX XXXX XXXX
instead write this,
func textFieldDidEndEditing(_ textField: UITextField) {
//Bank Account textfield
if textField.tag == 2
{
self.arrPersonalDetails[2].value = self.arrPersonalDetails[2].value
}
//Re-enter Bank Account textfield
if textField.tag == 3
{
self.arrPersonalDetails[3].value = self.arrPersonalDetails[3].value
}
}
Happy Coding!!!
Upvotes: 0
Reputation: 16774
If I understand correctly your requirement is to create a custom secure text field. Requirements are:
In addition it would be nice that all text field functionality would work, including copy-paste, selecting, inserting code in middle of text...
I created a new project and quickly ended with this:
class ViewController: UIViewController {
@IBOutlet private var textField: UITextField?
private var actualText: String = ""
private var maskedText: String = ""
private var masked: Bool = true {
didSet {
refreshTextField()
}
}
@IBAction private func maskedTogglePressed(_ sender: Any) {
masked = !masked
}
private func refreshTextField() {
textField?.text = masked ? maskedText : actualText
}
}
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var newString = (actualText as NSString).replacingCharacters(in: range, with: string) // Apply new text
// Remove all whitespaces
newString = newString.replacingOccurrences(of: " ", with: "")
// Remove all that is not a number
newString = newString.filter("0123456789".contains)
// Split string into components of max 4
var components: [String] = {
var toDeplete = newString
var components: [String] = []
while !toDeplete.isEmpty {
let length = min(toDeplete.count, 4)
components.append(String(toDeplete.prefix(length)))
toDeplete.removeFirst(length)
}
return components
}()
// Limit to maximum of 3 components
if components.count > 3 {
components = Array(components[0..<3])
}
// Generate masked components, replacing all characters with X
let maskedComponents: [String] = components.map { String($0.map { character in return "X" }) }
// Add spaces
newString = components.joined(separator: " ")
let maskedText = maskedComponents.joined(separator: " ")
// Assign new strings
self.actualText = newString
self.maskedText = maskedText
// Refresh field
refreshTextField()
// Disallow text field to apply it's change
return false
}
}
Could use some improvements when dealing with strings. But it does the job.
Code is commented and should give more information about the solution.
Upvotes: 1