UIAlertController: 在按下按钮之前检查多个UITextFields中是否有文本。

huangapple go评论57阅读模式
英文:

UIAlertController: Check if there's text in multiple UITextFields before button is pressed

问题

Swift 5,Xcode 14.2

我有两个UIViewController,它们都必须显示相同的UIAlertController(一次只加载一个)。我不想重复使用相同的代码,所以我将它添加到了已经用于传递数据的现有静态类中(一次只被一个线程访问)。

在我的静态类中:

static func createLoginDialog(buttonPressed: @escaping (_ username:String, _ pw:String) -> UIAlertController {
    let alert = UIAlertController(title: "Login", message: "", preferredStyle: .alert)

    alert.addAction(UIAlertAction(title: "Log in", style: .default, handler: { (action: UIAlertAction!) in
        let u = alert.textFields![0].text!
        let p = alert.textFields![1].text!
        buttonPressed(u,p)
    }))
        
    alert.addTextField { textField in
        textField.placeholder = "Enter Username"
    }
    
    alert.addTextField { textField in
        textField.placeholder = "Enter Password"
    }
    
    return alert
}

UIViewController类中的调用代码:

func doSomethingAndShowAlert() {
    //做一些事情

    let alert = MyStaticClass.createLoginDialog() {
        (username,pw) in
        //处理非空输入
    }
    self.present(alert, animated: true, completion: nil)
}

使用这段代码,无论输入是什么,一旦按下按钮,对话框都会被关闭。输入的有效性将在调用函数中进行检查,但我不希望关闭对话框,除非两个UITextField都有文本。根据其他问题的答案,你无法阻止对话框自动关闭,但你可以禁用操作/按钮,直到有文本输入,这对我来说是可以接受的。

这里有一些建议链接(仅适用于单个UITextField),但如果我将其添加到我的代码中(因为我的类是静态的),Xcode会抱怨@objc/action: #selector。我还尝试过将这个单个UITextField版本与这个多个UITextField版本结合起来,但动作函数从未被调用(可能是因为我没有在viewDidLoad()中设置它)。

我知道使用自定义视图而不是UIAlertController来显示会更容易,但我想尝试这种方式。

在我的静态类/函数中,如何在启用按钮之前检查两个UITextField中是否有文本?

英文:

Swift 5, Xcode 14.2

I've got two UIViewControllers that both have to display the same UIAlertController (only one is loaded at a time). Instead of using the same code twice, I added it to my existing static class that's already used to pass data around (only accessed by one thread at a time).

In my static class:

static func createLoginDialog(buttonPressed: @escaping (_ username:String, _ pw:String) -> Void) -> UIAlertController {
    let alert = UIAlertController(title: "Login", message: "", preferredStyle: .alert)

    alert.addAction(UIAlertAction(title: "Log in", style: .default, handler: { (action: UIAlertAction!) in
        let u = alert.textFields![0].text!
        let p = alert.textFields![1].text!
        buttonPressed(u,p)
    }))
        
    alert.addTextField { textField in
        textField.placeholder = "Enter Username"
    }
    
    alert.addTextField { textField in
        textField.placeholder = "Enter Password"
    }
    
    return alert
}

Calling code in the UIViewController classes:

func doSomethingAndShowAlert() {
    //Do something

    let alert = MyStaticClass.createLoginDialog() {
        (username,pw) in
        //Do stuff with non-empty input
    }
    self.present(alert,animated: true, completion: nil)
}

With this code the dialog is dismissed once you press the button, no matter what the input is. The input is checked for validity in the calling function but I don't want to close the dialog, unless there's text in both UITextFields. According to answers to other questions, you can't keep the dialog from being dismissed automatically but you can disable the action/button until there's text, which is fine with me.

There are a couple of suggestions here (only for a single UITextField) but Xcode complains about the @objc/action: #selector if I add it to my code (because my class is static?). I also tried to combine this single TF version with this multi TF version but the action function is never called (maybe because I don't set it in viewDidLoad()?).

I'm aware that this would be a lot easier with a custom view that's displayed instead of a UIAlertController but I want to try it this way.

How do I check if there's text in both UITextFields, before enabling the button, in my static class/function?

答案1

得分: 1

Here's the translated code portion:

快速编写代码以使用您的静态方法逻辑:

static func createLoginDialog(buttonPressed: @escaping (_ username:String, _ pw:String) -> Void) -> UIAlertController {
    let alert = UIAlertController(title: "Login", message: "", preferredStyle: .alert)
    
    let loginAction = UIAlertAction(title: "Log in", style: .default, handler: { _ in
        let u = alert.textFields![0].text!
        let p = alert.textFields![1].text!
        buttonPressed(u,p)
    })
    
    alert.addAction(loginAction)
    loginAction.isEnabled = false
    
    var textFields: [UITextField] = []
    
    alert.addTextField { textField in
        textField.placeholder = "Enter Username"
        textFields.append(textField)
        NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification,
                                               object: textField,
                                               queue: .main) { _ in
            loginAction.isEnabled = textFieldsAreValid(textFields)
        }
    }
    
    alert.addTextField { textField in
        textField.placeholder = "Enter Password"
        textFields.append(textField)
        NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification,
                                               object: textField,
                                               queue: .main) { _ in
            loginAction.isEnabled = textFieldsAreValid(textFields)
        }
    }
    
    func textFieldsAreValid(_ textFields: [UITextField]) -> Bool {
        textFields.allSatisfy { !($0.text ?? "").isEmpty } //Do the needed tests, I just check if not empty
    }
    
    return alert
}

Please note that I've translated the code part as requested, and I've excluded the translation of the other content and the question you mentioned. If you need further assistance or have more specific translation requests, please feel free to ask.

英文:

Quickly written to have the logic using your static method:

static func createLoginDialog(buttonPressed: @escaping (_ username:String, _ pw:String) -> Void) -> UIAlertController {
    let alert = UIAlertController(title: "Login", message: "", preferredStyle: .alert)
    
    let loginAction = UIAlertAction(title: "Log in", style: .default, handler: { _ in
        let u = alert.textFields![0].text!
        let p = alert.textFields![1].text!
        buttonPressed(u,p)
    })
    
    alert.addAction(loginAction)
    loginAction.isEnabled = false
    
    var textFields: [UITextField] = []
    
    alert.addTextField { textField in
        textField.placeholder = "Enter Username"
        textFields.append(textField)
        NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification,
                                               object: textField,
                                               queue: .main) { _ in
            loginAction.isEnabled = textFieldsAreValid(textFields)
        }
    }
    
    alert.addTextField { textField in
        textField.placeholder = "Enter Password"
        textFields.append(textField)
        NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification,
                                               object: textField,
                                               queue: .main) { _ in
            loginAction.isEnabled = textFieldsAreValid(textFields)
        }
    }
    
    func textFieldsAreValid(_ textFields: [UITextField]) -> Bool {
        textFields.allSatisfy { !($0.text ?? "").isEmpty } //Do the needed tests, I just check if not empty
    }
    
    return alert
}

But as Vadian said, it maybe be better to have an extension on UIViewController.

We can imagine that you have

protocol LoginProtocol: Protocol {
}

extension LoginProtocol where Self: UIViewController {
  
    func loginAlert(buttonPressed: @escaping (_ username:String, _ pw:String) -> Void) -> UIAlertController {
        //The previous code
    }

    func showLoginAlert(buttonPressed: @escaping (_ username:String, _ pw:String) -> Void) {
        let alert = loginAlert(buttonPressed: buttonPressed)
        present(alert, animated: true)
    }
}

I know that sometimes, you want to call it from classes that aren't UIViewController, but in theory, that object (be it a subview etc.), should tell its parent ViewController to show the login alert, it shouldn't do it itself. That's why I'd prefer using that way.

And on each ViewController that can show the alert:

extension MyCustomVC: LoginProtocol {}

And it can call showLoginAlert(...).

huangapple
  • 本文由 发表于 2023年5月24日 21:40:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324187.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定