Well, I think I found a much simpler solution:
- create a background image with the text for placeholder
- then, when you type, the new text will appear just over the placeholder.
I've left some links to additional information if needed
Here is a sample code:
class TextFieldDynPlaceHolder: UITextField, UITextFieldDelegate {
let defaultPlaceHolder = "DD MM YYYY"
var thePlaceHolder : String! // To be set at call
func commonInit() {
delegate = self
thePlaceHolder = defaultPlaceHolder // "DD MM YYYY"
self.background = image(from: thePlaceHolder)
// Need to set borderStyle: https://stackoverflow.com/questions/64057501/how-to-fix-uitextfield-background-image-not-displayed-after-ios14
self.borderStyle = .line
}
// https://stackoverflow.com/questions/759658/uitextview-background-image
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
// https://stackoverflow.com/questions/51100121/how-to-generate-an-uiimage-from-custom-text-in-swift
func image(from text: String?) -> UIImage? {
let frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
let nameLabel = UILabel(frame: frame)
nameLabel.textAlignment = .left
nameLabel.backgroundColor = .systemBackground
nameLabel.textColor = .placeholderText // .darkGray if .placeholderText is too light
nameLabel.font = self.font
nameLabel.text = text
UIGraphicsBeginImageContext(frame.size)
if let currentContext = UIGraphicsGetCurrentContext() {
nameLabel.layer.render(in: currentContext)
let nameImage = UIGraphicsGetImageFromCurrentImageContext()
return nameImage
}
return nil
}
// Check we typed digits or space or backspace
func textFieldDidChangeSelection(_ textField: UITextField) {
var trimmedPlaceHolder = defaultPlaceHolder
if let typedText = textField.text {
for c in typedText.unicodeScalars {
if !CharacterSet.decimalDigits.contains(c) && !CharacterSet.whitespaces.contains(c) {
// And play a beep
textField.text = String(textField.text!.dropLast())
}
}
}
if let typed = textField.text?.count, typed > 0 {
trimmedPlaceHolder = String(defaultPlaceHolder.dropFirst(typed))
// And we replace with spaces
for _ in 0..<typed {
trimmedPlaceHolder = " " + trimmedPlaceHolder
}
self.background = image(from: trimmedPlaceHolder)
} else {
self.background = image(from: trimmedPlaceHolder)
}
}
// Could check here the complete date Validity
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return true // Here you could test the validity of entry as a Date
}
}
To test it:
- in storyboard, create a UITextField and declare as TextFieldDynPlaceHolder
- select a fixed width font (otherwise, date will nor superpose placeholder)
- set its width to what you need (don't let it free, otherwise placeholder may not show)
- in the controller, set thePlaceHolder to the date you need
- May set thePlaceHolder in commonInit() (change from = "DD MM YYYY")
If that works, thanks to tell as well as to post any improvement you may find, like better testing the validity of what was typed.
And then don't forget to close the thread.