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

b5buobof  于 2023-06-04  发布在  Swift
关注(0)|答案(1)|浏览(114)

Swift 5,Xcode 14.2
我有两个UIViewController,它们都必须显示相同的UIAlertController(一次只能加载一个)。我没有两次使用相同的代码,而是将其添加到已经用于传递数据的现有静态类中(一次只能由一个线程访问)。
在我的静态类中:

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
}

调用UIViewController类中的代码:

func doSomethingAndShowAlert() {
    //Do something

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

使用此代码,无论输入是什么,只要按下按钮,对话框就会关闭。在调用函数中检查输入的有效性,但我不想关闭对话框,除非两个UITextField中都有文本。根据其他问题的答案,你不能保持对话框自动关闭,但你可以禁用操作/按钮,直到有文本,这对我来说很好。
有几个建议here(仅适用于单个UITextField),但如果我将其添加到代码中,Xcode会抱怨@objc/action: #selector(因为我的类是静态的?)).我也试过把this单TF版本和this多TF版本结合起来,但是action函数从来没有被调用过(也许是因为我没有在viewDidLoad()中设置它?).
我知道使用自定义视图而不是UIAlertController会更容易,但我想尝试这种方式。
在我的静态类/函数中,在启用按钮之前,我如何检查两个UITextField中是否有文本?

2wnc66cl

2wnc66cl1#

使用你的静态方法快速编写有逻辑:

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
}

但正如Vadian所说,在UIViewController上扩展可能更好。
我们可以想象你有

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)
    }
}

我知道有时候,你想从不是UIViewController的类中调用它,但理论上,该对象(无论是子视图等)应该告诉其父ViewController显示登录警报,而不是它自己。这就是为什么我更喜欢使用这种方式。
在每个可以显示警报的ViewController上:

extension MyCustomVC: LoginProtocol {}

它可以调用showLoginAlert(...)

相关问题