Reputation: 255
I have an UIAlertController
with a textField
. Since I can't create an outlet for the textField
I have to find another way to get ahold of a function that observes user input in realtime.
I have made some attempts, but not successfully. This thread explain how to add a target to the textField:
How do I check when a UITextField changes?
textfield.addTarget(self, action: #selector(ViewControllerr.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)
I tried this, but don't quite understand the #selector
input. Does it want my UIViewController?
or my UIAlertController
I tried both but got the same error message:
Value of type 'UIViewController' has no member 'textFieldDidChange'
Same with UIAlertController
.
I also tried setting my textField
to delegate to access functions of that class:
textField.delegate = self as? UITextFieldDelegate
But I guess this isn't the correct approach as I still couldn't access any functions except by doing:
textField.delegate?.textFieldDidBeginEditing?(textField)
But that didn't give me anything either.
I also tried adding an extension like this:
extension UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
print("🦋EDITING")
}// became first responder
func textFieldDidEndEditing(_ textField: UITextField) {
print("🦋EDITING")
}
}
But for some reason I can't access them through textView
If I extend the class like this:
extension PickNumberViewController: UITextFieldDelegate {...}
I get the same error message
Value of type 'UITextField' has no member 'textFieldDel...'
which makes no sense since I set textField
to UITextFieldDelegate
not UITextField
.
Let me know if you would like to see my code as well.
Upvotes: 1
Views: 816
Reputation: 2324
iOS13+ One option is to use UIAction
extension UIAlertController {
public func addTextField(_ configuration: @escaping ((UITextField) -> Void),
onEditingDidBegin: ((UITextField) -> Void)? = nil,
onEditingChanged: ((UITextField) -> Void)? = nil,
onEditingDidEnd: ((UITextField) -> Void)? = nil) {
self.addTextField { textField in
configuration(textField)
if let onEditingDidBegin {
textField.addAction(UIAction { action in
onEditingDidBegin(action.sender as! UITextField)
}, for: .editingDidBegin)
}
if let onEditingChanged {
textField.addAction( UIAction { action in
onEditingChanged(action.sender as! UITextField)
}, for: .editingChanged)
}
if let onEditingDidEnd {
textField.addAction( UIAction { action in
onEditingDidEnd(action.sender as! UITextField)
}, for: .editingDidEnd)
}
}
}
Use like so:
let alert = UIAlertController(title: "Enter Text", message: nil, preferredStyle: .alert)
alert.addTextField { textField in
textField.text = "Some text"
textField.placeholder = "Placeholder"
} onEditingDidBegin: { textField in
textField.selectAll(nil) // selects all text
} onEditingChanged: { textField in
if let text = textField.text {
textField.textColor = text.count > 5 ? .red : .label
}
} onEditingDidEnd: { textField in
if let text = textField.text {
if text.count > 5 {
textField.text = "This is too long"
}
}
}
You shouldn't use onEditingDidEnd
for your final validation though. Put that in the UIAlertAction
for your "OK" button and pull out the textFields:
let okAction = UIAlertAction(title: "OK", style: .destructive) { [weak self, weak alert] _ in
if let self,
let alert,
let textField = alert.textFields?.first {
// validate (here, 'self', is a view controller)
self.label.text = textField.text
}
}
alert.addAction(okAction)
Upvotes: 0
Reputation: 15748
You don't need to hold a reference to the text field. You can add target or set delegate within the addTextField
closure like this.
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alert.addTextField { textField in
textField.delegate = self
textField.addTarget(self, action: #selector(self.textChanged(_:)), for: .editingChanged)
textField.placeholder = "Enter name"
}
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
@objc func textChanged(_ sender: UITextField) {
print("Text changed - ", sender.text!)
}
}
extension ViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
print("EDITING")
}
func textFieldDidEndEditing(_ textField: UITextField) {
print("EDITING")
}
}
Upvotes: 0
Reputation: 100503
You can try to get a reference to it with self.textF = alert.textFields?[0]
class ViewController: UIViewController {
var textF:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
//1. Create the alert controller.
let alert = UIAlertController(title: "Some Title", message: "Enter a text", preferredStyle: .alert)
alert.addTextField { (textField) in
textField.text = "Some default text"
}
let tex = alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
}))
// 4. Present the alert.
self.present(alert, animated: true, completion: nil)
self.textF = alert.textFields?[0]
self.textF.addTarget(self, action: #selector(self.textFieldDidChange), for: UIControl.Event.editingChanged)
}
}
@objc func textFieldDidChange() {
print(textF.text)
}
}
Upvotes: 4