Reputation: 70
I have created a UITextfield
where i want to add 4 digit password, but once I enter the first digit whole placeholder disappear. how can I keep next three placeholder letters in UITextfield
while entering next password letters.
here is my screenshot:
Upvotes: 1
Views: 1363
Reputation: 356
Swift 4
This is a modified version of @Denislava Shentova's answer that I believe simplifies into less lines of code, fixes issues, and makes the code more readable.
Not Fully Tested
import UIKit
class YourClass: UIViewController {
//It is also a good idea to deny users the ability to paste into this textField.
//set up textField
let textField : UITextField = {
let textField = UITextField()
// These are 'long dashes' you can replace them with whatever you would like.
// These look best IMO.
textField.text = "——————" //No spaces needed!
textField.textColor = .black
textField.textAlignment = .center
textField.tintColor = .clear //this will hide the cursor.
return textField
}()
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
//This sets the spacing or 'Kern' of the textfield. Adjust the value: 10.0 and the fontSize to get the desired output.
textField.defaultTextAttributes.updateValue(10.0, forKey: NSAttributedStringKey.kern.rawValue)
}
}
extension YourClass : UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//get the current text of the textField.
guard let text = textField.text else { return false }
//handle backspace event.
if string == "" {
guard let indexToReplace = text.index(text.startIndex, offsetBy: range.location, limitedBy: text.endIndex) else { return false }
textField.text?.remove(at: indexToReplace)
textField.text?.insert("—", at: indexToReplace)
//adjust cursor position
if let newPostion = textField.position(from: textField.beginningOfDocument, offset: range.location) {
textField.selectedTextRange = textField.textRange(from: newPostion, to: newPostion)
return false
}
}
//handle character entered event.
if range.location + 1 <= text.count,
let end = text.index(text.startIndex, offsetBy: range.location + 1, limitedBy: text.endIndex),
let start = text.index(text.startIndex, offsetBy: range.location, limitedBy: text.endIndex) {
textField.text = textField.text?.replacingOccurrences(of: "—", with: string, options: .caseInsensitive, range: Range(uncheckedBounds: (lower: start, upper: end)))
}
//adjust cursor position.
if range.location + 1 < text.count {
if let newPosition = textField.position(from: textField.beginningOfDocument, offset: range.location + 1){
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
}
return false
}
//make sure to start at the begining of the textField.
func textFieldDidBeginEditing(_ textField: UITextField) {
let newPosition = textField.beginningOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
}
Upvotes: 0
Reputation: 741
I wanted to try SeanLintern88's solution because it sounded like a little challange. And it is in the case where the text field should have spaces between the underscore.
textField.text = "_ _ _ _"
This is the solution I came with and while it was fun to write, this is something I do not recommend using in real projects. Better try the 4 separate text fields approach :)
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return false }
var location = range.location
//is deleting
if string == "" {
guard let indexToReplace = text.index(text.startIndex, offsetBy: location, limitedBy: text.endIndex) else {
return false
}
let isCharDeleted = location % 2 == 0
//place " " or "_" depending on the position that was deleted
let stringToReplaceWith = isCharDeleted ? "_" : " "
let charachter = stringToReplaceWith[stringToReplaceWith.startIndex]
textField.text?.remove(at: indexToReplace)
textField.text?.insert(charachter, at: indexToReplace)
var newCursorPositionOffset = location
//deletetion occured on space " "
if !isCharDeleted {
guard let previousIndex = text.index(text.startIndex, offsetBy: location-1, limitedBy: text.endIndex) else {
return false
}
//delete the previous charachter
textField.text?.remove(at: previousIndex)
let dash = "_"
let char = dash[dash.startIndex]
textField.text?.insert(char, at: previousIndex)
//correct cursor position
newCursorPositionOffset -= 1
}
//move cursor position
let newPosition = textField.position(from: textField.beginningOfDocument, offset: newCursorPositionOffset)
textField.selectedTextRange = textField.textRange(from: newPosition!, to: newPosition!)
return false
}
//is typing
if range.location + 1 <= text.characters.count,
let end = text.index(text.startIndex, offsetBy: location+1, limitedBy: text.endIndex),
let start = text.index(text.startIndex, offsetBy: location, limitedBy: text.endIndex) {
textField.text = textField.text?.replacingOccurrences(of: "_", with: string, options: .caseInsensitive, range: Range(uncheckedBounds: (lower: start, upper: end)))
//correct the cursor position if placed on " " index
if range.location % 2 != 0 {
location -= 1
}
}
//skip " " and move cursor to the next "_"
if location+2 < text.characters.count {
let newPosition = textField.position(from: textField.beginningOfDocument, offset: location+2)
textField.selectedTextRange = textField.textRange(from: newPosition!, to: newPosition!)
}
return false
}
func textFieldDidBeginEditing(_ textField: UITextField) {
let newPosition = textField.beginningOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
}
PS: Probably it can be made shorter with better choosing of functions for string operations, but still a pretty .. unfriendly solution.
Conclusion: DON'T DO THIS AT HOME :D
Upvotes: 1
Reputation: 3141
Instead of using placeHolder
, just override the shouldChangeCharactersInRange
method and append the characters to the string until the string is 4 characters long, you can also use attributed string if you want the _
to look different to the dots.
Upvotes: 1