【问题标题】:Show Alert on ViewController from external static function从外部静态函数在 ViewController 上显示警报
【发布时间】:2018-07-26 18:36:23
【问题描述】:

我有一个注册视图控制器,它调用 Service 类并使用其 static func signUp(...) 将某人注册到数据库。

如果注册不成功,应通过showAlert(...) 静态方法显示警报。

但是,正在发生的事情是记录了不成功的注册,但我当前的方法没有显示警报。

Attempt to present <UIAlertController: xx> on <Yyy:SignUpViewController: ss> whose view is not in the window hierarchy!

目前,我正在尝试将 SignUpViewController 作为参数 (vc: UIViewController) 传递给静态方法,然后调用 Service.showAlert(on: vc, ...)

我还尝试将showAlert(...) 方法合并到SignUpViewController 类中作为extension UIViewController,并从Service signUp() 作为vc.showAlert(...) 调用它。我收到与上述相同的错误。

重要的是,我想将代码重用于来自不同视图控制器的数据库调用,因此我不想重写代码并将其放置在每个视图控制器中。这不仅仅是为了注册。我希望在外部类中调用这些数据库。

代码:

服务类

static func signUp(email: String, password: String, vc: UIViewController) {
        Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
            if let error = error {
                print("Failed to sign in with error ", error)
                Service.showAlert(on: vc, style: .alert, title: "Sign-in Error", message: error.localizedDescription)
                return
            }
            // ...code
        }
        // ...code
}

// other methods

static func showAlert(on: UIViewController, style: UIAlertControllerStyle, title: String?, message: String?, actions: [UIAlertAction] = [UIAlertAction(title: "Ok", style: .default, handler: nil)], completion: (() -> Swift.Void)? = nil) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: style)
        for action in actions {
            alert.addAction(action)
        }
        on.present(alert, animated: true, completion: completion)
}

SignUpViewController 类方法调用

Service.signUp(email: emailTextField.text!, password: passwordTextField.text!, vc: self)

编辑:

如果我从 SignUpViewController 中执行登录功能,则会显示警报:

@IBAction func btnActionLogin(_ sender: Any) {
                    Auth.auth().createUser(withEmail: self.emailTextField.text!, password: self.passwordTextField.text!) { (authResult, error) in
                        if let error = error {
                            print("Failed to create new user with error ", error)
                            Service.showAlert(on: self, style: .alert, title: "Account Creation Error", message: error.localizedDescription)
                            return
                        } else {
                        // ... more code
                    }

}

编辑 2:

我实施的方法实际上很好。是我在错误的地方执行切换视图控制器!我不小心切换了视图控制器,暗示登录成功,即使它没有成功。

【问题讨论】:

    标签: ios swift uiviewcontroller uialertcontroller


    【解决方案1】:

    如果不查看 Auth 文档,我无法 100% 确定解决方案,但我认为函数Auth.auth().signIn(withEmail: email, password: password) 在后台线程上执行。

    错误Attempt to present <UIAlertController: xx> on <Yyy:SignUpViewController: ss> whose view is not in the window hierarchy! 可能是因为您正在从该后台线程调用Service.showAlert(on: vc, style: .alert, title: "Sign-in Error", message: error.localizedDescription)。当后台线程执行您的命令以向用户显示警报时,视图很可能不再在堆栈上。

    在主线程上运行任何影响 UI 的代码很重要。

    我建议您在主线程上异步执行代码,方法是用DispatchQueue.main.async 包装该行代码,如下所示:

    DispatchQueue.main.async { Service.showAlert(on: vc, style: .alert, title: "Sign-in Error", message: error.localizedDescription) }

    希望这能解决问题。如果不回来报告,我会试着想想还有什么事情发生。

    更新:回答有关显示警报的正确方法的问题:

    “目前,我正在尝试将 SignUpViewController 作为 参数(vc:UIViewController)到静态方法,然后调用 Service.showAlert(on: vc, ...)。我不认为这是正确的 接近。”

    我使用调用静态方法的相同方法。我认为这种范式没有问题。我在项目中通常这样做的方式如下:

    我创建了一个类似于你的静态方法来处理在视图控制器上呈现警报。

    /** Easily Create, Customize, and Present an UIAlertController on a UIViewController
    
     - Parameters:
        - target: The instance of a UIViewController that you would like to present tye UIAlertController upon.
        - title: The `title` for the UIAlertController.
        - message: Optional `message` field for the UIAlertController. nil by default
        - style: The `preferredStyle` for the UIAlertController. UIAlertControllerStyle.alert by default
        - actionList: A list of `UIAlertAction`. If no action is added, `[UIAlertAction(title: "OK", style: .default, handler: nil)]` will be added.
    
     */
    func showAlert(target: UIViewController, title: String, message: String? = nil, style: UIAlertControllerStyle = .alert, actionList:[UIAlertAction] = [UIAlertAction(title: "OK", style: .default, handler: nil)] ) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: style)
        for action in actionList {
            alert.addAction(action)
        }
        // Check to see if the target viewController current is currently presenting a ViewController
        if target.presentedViewController == nil {
            target.present(alert, animated: true, completion: nil)
        }
    }
    

    然后我从我的视图控制器中调用该函数,如下所示:showAlert(target: self, title: "Error", message: "Some error message")

    【讨论】:

    • 您是对的,身份验证是在后台执行的,但据我所知,这不会影响它。如我错了请纠正我。如果我从 SignUpViewController 中功能性地执行所有登录并从那里调用服务 showAlert 它工作正常并显示警报。我将编辑帖子以说明我的意思。
    • 我已经意识到我的错误。请参阅编辑 2。我的方法一直都很好。这是一个不同的错误。我已经接受了你的回答,因为它与我正在做的事情相似。
    猜你喜欢
    • 1970-01-01
    • 2021-02-01
    • 2017-05-03
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-02
    • 2021-04-30
    相关资源
    最近更新 更多