I made a subclass of a UIView with a text field and a button which allows to configure if text field is secured via isSecureTextEntry property. I am using two instances of that view one for setting a password and another one for confirming it in a view controller like this
let passwordTextField = PasswordTextFieldView(placeholder: "New password")
let confirmPasswordTextField = PasswordTextFieldView(placeholder: "Confirm password")
Text field subclass code
final class PasswordTextFieldView: UIView {
lazy var textField: UITextField = {
let textField = UITextField()
textField.textColor = .black
textField.placeholder = placeholder
textField.textAlignment = .left
textField.textContentType = .password
textField.autocorrectionType = .no
textField.isSecureTextEntry = true
return textField
}()
private let securedButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(systemName: "eye.slash.fill")?.withRenderingMode(.alwaysTemplate), for: .normal)
button.tintColor = .gray
button.addTarget(self, action: #selector(securedButtonTapped), for: .touchUpInside)
return button
}()
private var isSecured: Bool = true
var placeholder: String
required init(placeholder: String) {
self.placeholder = placeholder
super.init(frame: CGRect.zero)
// Layout views
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func securedButtonTapped() {
isSecured.toggle()
securedButton.setImage(UIImage(systemName: isSecured ? "eye.slash.fill" : "eye.fill"), for: .normal)
textField.isSecureTextEntry = isSecured
}
}
So the problem is, that tapping a button changes isSecureTextEntry on a textField which is being edited. How to fix it?
When you create your let
variable with a self calling block, you shouldn't use self
as it's not determined at that moment. You can add print to see it's value, it's not your view, because the view is not yet created.
Sometimes it'll be correct value at the end(I tried running your code and it run fine on my simulator), and sometimes it's not.
You have two options in such cases:
let
with lazy var
. In this case self
if always gonna be determinedprivate lazy var securedButton: UIButton = {
...
}
init
. If you're using many inits don't forget to add to all of them, preferably moving to an other function. I prefer this way to make sure all my props are let
if I don't need to change it. Like this:required init(placeholder: String) {
self.placeholder = placeholder
super.init(frame: CGRect.zero)
initialize()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initialize()
// I know you're not using this initializer, just in case you'd need in future
}
private func initialize() {
securedButton.addTarget(self, action: #selector(securedButtonTapped), for: .touchUpInside)
}