从外部静态函数显示 ViewController 上的警报
Show Alert on ViewController from external static function
我有一个注册视图控制器,它调用 Service
class 并使用其 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
class 中作为 extension UIViewController
并从 Service
signUp()
中调用它作为 vc.showAlert(...)
。我收到与上述相同的错误。
重要的是,我想重用来自不同视图控制器的数据库调用代码,所以我不想重写代码并将其放在每个视图控制器中。这不仅仅是为了注册。我想在外部 class.
中调用这些数据库
代码:
服务class
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)
}
SignUpViewControllerclass方法调用
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:
我实施的方法实际上很好。是我在错误的地方执行切换视图控制器!我不小心切换了视图控制器,这意味着登录成功,即使它没有成功。
如果不查看 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)
}
希望这能解决问题。如果不反馈,我会试着想想还有什么事情发生。
更新:回答有关显示警报的正确方法的问题:
"Currently, I am trying to pass the SignUpViewController as a
parameter (vc: UIViewController) to the static method, then calling
Service.showAlert(on: vc, ...). I don't think this is the correct
approach."
我使用调用静态方法的相同方法。我认为这种范例没有问题。我通常在我的项目中这样做的方式如下:
我创建了一个类似于您的静态方法来处理在视图控制器上显示警报。
/** 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")
我有一个注册视图控制器,它调用 Service
class 并使用其 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
class 中作为 extension UIViewController
并从 Service
signUp()
中调用它作为 vc.showAlert(...)
。我收到与上述相同的错误。
重要的是,我想重用来自不同视图控制器的数据库调用代码,所以我不想重写代码并将其放在每个视图控制器中。这不仅仅是为了注册。我想在外部 class.
中调用这些数据库代码:
服务class
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)
}
SignUpViewControllerclass方法调用
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:
我实施的方法实际上很好。是我在错误的地方执行切换视图控制器!我不小心切换了视图控制器,这意味着登录成功,即使它没有成功。
如果不查看 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)
}
希望这能解决问题。如果不反馈,我会试着想想还有什么事情发生。
更新:回答有关显示警报的正确方法的问题:
"Currently, I am trying to pass the SignUpViewController as a parameter (vc: UIViewController) to the static method, then calling Service.showAlert(on: vc, ...). I don't think this is the correct approach."
我使用调用静态方法的相同方法。我认为这种范例没有问题。我通常在我的项目中这样做的方式如下:
我创建了一个类似于您的静态方法来处理在视图控制器上显示警报。
/** 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")