实例化 ViewController 的按钮在隐藏后不起作用

Button to instanciate ViewController is not working after hiding it

我刚发现这个非常奇怪的问题...

我有这个 button 触发了这个 function:

@objc func vergessenTapped() {
    let forgotPasswordVC = self.storyboard?.instantiateViewController(withIdentifier: "ForgotPasswordVC") as! ForgotPasswordVC
    forgotPasswordVC.email = self.emailTextField.text!
    self.present(forgotPasswordVC, animated: true, completion: nil)
}

我还有hide/show上面button的这些功能:

    // delegate Methode für eye-Button
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    switch textField {
    case passwordTextField:
        if passwordTextField.text != "" {
            eyeButton.isHidden = false
            vergessenButton.isHidden = true
        } else {
            eyeButton.isHidden = true
        }

        break
    default:
        break
    }
    return true
}
// delegate Methode für eye-Button
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
    
    if textField == passwordTextField {
        self.eyeButton.isHidden = true
        self.vergessenButton.isHidden = false
    }
    return true
}

@objc func textFieldDidChange(_ textField: UITextField) {
    if textField.text == "" {
        self.eyeButton.isHidden = true
        self.vergessenButton.isHidden = false
    }else {
        self.vergessenButton.isHidden = true
        self.eyeButton.isHidden = false
    }
}

现在问题:

当我在上面的任何 delegate-function 隐藏它之前第一次按 button 时,ViewControllerpresents 工作得很好。

但是:一旦按钮第一次被隐藏,并且用户在它再次可见后单击它,forgotpasswordViewController 的行为真的很奇怪,例如:

1. 调用 self.dismiss 时,`ViewController 实际上并没有被关闭,而是弹出下面的 它。

@objc func backButtonTapped(){
    self.dismiss(animated: true, completion: nil)
}

2. forgotpasswordViewController 中的 button(只有一个按钮)没有任何作用。

这里有一个Screenvideo可以更好的理解。进入视频 22 秒后,我开始打字(不知何故在屏幕视频上不可见,但您可以看到“vergessen”但消失并再次出现。再次出现后,我单击它,如您所见,backButtonconfirmButton 像以前一样工作...

这是某种错误吗???我无法解释..所以如果有人帮助我,我将不胜感激!

演示项目:

Demo-Project

与Vergessen按钮的显示和隐藏无关。问题是你的 ForgotPassword 视图控制器(实际上也是你的其他视图控制器)中的这类事情:

let backButton: UIButton = {
    let v = UIButton()
    v.translatesAutoresizingMaskIntoConstraints = false
    v.setImage(UIImage(named:"down"), for: .normal)
    v.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
    return v
}()

这有点不对劲。你看到它是什么了吗?当然不是,因为这是所有 iOS 编程中最讨厌的小陷阱之一。您不能在实例 属性 的初始化程序中说 v.addTarget(self...。为什么?因为实例 self(此处为视图控制器)是我们正在初始化的对象。所以 self 在这里没有任何意义。好吧,有时它有意义,有时却没有;真正的陷阱是代码 ever 有效。那真的误导你了。 (你也被代码编译的事实误导了。在我看来,它不应该。我有一个filed a bug,如果它有任何安慰的话。)

好吧,所以我可以想到很多解决方案,但传统上我们在这里做的是用 lazy var 替换 let。我想我在你的代码中看到了六个地方你需要这样做。

lazy var backButton: UIButton = { // ... and so on

当你这样做时,一切都会开始正常工作。

解决此问题的原因是 lazy 推迟了该代码的 运行 直到 视图控制器本身已初始化之后。那么 self 就是它应该表达的意思。

(很抱歉没有lazy let,但我对此无能为力。你只需要说lazy var然后就这样吧。)

我还要补充一点,iOS 14 中的新功能,您可以(如果愿意)停止调用 addTarget(_:action:for:),这样您就不会再掉入这个陷阱。但是,向按钮添加 UIAction 的修复仅适用于 iOS 14 及更高版本。