如何显示来自不同班级的警报

Posted

技术标签:

【中文标题】如何显示来自不同班级的警报【英文标题】:How to display an Alert from a different class 【发布时间】:2017-09-13 02:14:55 【问题描述】:

我创建了一个 Utilities 类来保存一些常用函数,其中之一是 alertUser 函数,如果调用该函数,将向用户显示一个带有提供的标题和消息文本的警报框。在另一个类文件中,我正在验证一些文本字段条目,如果验证没有通过,那么我想使用 Utilities 类中的 alertUser 函数。但是,当我这样做时,我在 Xcode 日志中收到以下错误消息:

Warning: Attempt to present <UIAlertController: 0x7f9c4be0b140> on <MyAppName.Utilities: 0x7f9c4be1cb60> whose view is not in the window hierarchy!

调用代码在 UIViewController 类文件中。这是在 类 ItemSettingsVC: UIViewController:

private func validateNameField() -> Bool 
    var passed = false

    if (nameField.hasText) 
        passed = true
     else 
        Utilities().alertUser(strTitle: "Alert", strMessage: strInvalidNameFieldErrorMsg)
        passed = false
    
    return passed

这是 alertUser 函数,它位于 类实用程序:UIViewController:

public func alertUser(strTitle: String, strMessage: String) 
    let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
    let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
    myAlert.addAction(okAction)
    self.present(myAlert, animated: true, completion: nil)

这是在 ios 上运行的。我正在使用 Xcode 8 和 swift 3。非常感谢任何帮助。谢谢。

【问题讨论】:

我已经给出了答案,但我也只想在这里发表评论以避免混淆你应该做什么。要回答您有关崩溃的问题,这是因为您的 Utilities(它是 UIViewController 的子类)不是您前台的当前视图,它试图通过调用 self 来呈现警报控制器这就是为什么我推荐下面的答案。也请注意那里的最后一条消息:) 【参考方案1】:

应该这样做:

public func alertUser(strTitle: String, strMessage: String) 
    let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
    let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
    myAlert.addAction(okAction)
    UIApplication.shared.delegate?.window??.rootViewController?.present(myAlert, animated: true, completion: nil)

【讨论】:

【参考方案2】:

您必须在 alertUser 函数中添加一个附加参数,该参数将是显示警报控制器的 VC。

例如:

public func alertUser(strTitle: String, strMessage: String, viewController: UIViewController) 
    let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
    let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
    myAlert.addAction(okAction)
    viewController.present(myAlert, animated: true, completion: nil)

但我建议您只制作 UIViewControllerextension 并在其中添加您的 func alertUser()*,因为您肯定会使用在我看来,不同 VC 和复杂性方面的这个 alertUser 会更加优化。

像这样:

extension UIViewController 

  func showAlert(title: String, message: String, callback: @escaping () -> ()) 
     let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
     alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: 
       alertAction in
       callback()
     ))

     self.present(alert, animated: true, completion: nil)
   

   //add additional functions here if necessary
   //like a function showing alert with cancel

注意:请不要将 Utilities 类设为 UIViewController 的子类,最好将其设为 struct 处理静态函数和/或变量

【讨论】:

【参考方案3】:

使用这个类可以轻松显示 Alert 或 ActionSheet

UIAlertController 扩展

public extension UIAlertController 
    public func showAlert(animated: Bool = true, completionHandler: (() -> Void)? = nil) 
        guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else 
            return
        
        var forefrontVC = rootVC
        while let presentedVC = forefrontVC.presentedViewController 
            forefrontVC = presentedVC
        
        forefrontVC.present(self, animated: animated, completion: completionHandler)
    

为 UIAlertController Show 创建 AppAlert 类

public class AppAlert 
    private var alertController: UIAlertController

    public init(title: String? = nil, message: String? = nil, preferredStyle: UIAlertControllerStyle) 
        self.alertController = UIAlertController(title: title, message: message, preferredStyle: preferredStyle)
    

    public func setTitle(_ title: String) -> Self 
        alertController.title = title
        return self
    

    public func setMessage(_ message: String) -> Self 
        alertController.message = message
        return self
    

    public func setPopoverPresentationProperties(sourceView: UIView? = nil, sourceRect:CGRect? = nil, barButtonItem: UIBarButtonItem? = nil, permittedArrowDirections: UIPopoverArrowDirection? = nil) -> Self 

        if let poc = alertController.popoverPresentationController 
            if let view = sourceView 
                poc.sourceView = view
            
            if let rect = sourceRect 
                poc.sourceRect = rect
            
            if let item = barButtonItem 
                poc.barButtonItem = item
            
            if let directions = permittedArrowDirections 
                poc.permittedArrowDirections = directions
            
        

        return self
    

    public func addAction(title: String = "", style: UIAlertActionStyle = .default, handler: @escaping ((UIAlertAction!) -> Void) =  _ in ) -> Self 
        alertController.addAction(UIAlertAction(title: title, style: style, handler: handler))
        return self
    

    public func addTextFieldHandler(_ handler: @escaping ((UITextField!) -> Void) =  _ in ) -> Self 
        alertController.addTextField(configurationHandler: handler)
        return self
    

    public func build() -> UIAlertController 
        return alertController
    

用于打开警报框

AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .alert)
    .addAction(title: "NO", style: .cancel)  _ in
        // action
    
    .addAction(title: "Okay", style: .default)  _ in
         // action
    
    .build()
    .showAlert(animated: true)

用于 ActionSheet 打开

if UIDevice.current.userInterfaceIdiom != .pad 
    // Sample to show on iPhone
    AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .actionSheet)
        .addAction(title: "NO", style: .cancel) _ in
            print("No")
        
        .addAction(title: "YES", style: .default)  _ in
            print("Yes")
        
        .build()
        .showAlert(animated: true)
 else 
    // Sample to show on iPad
    AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .actionSheet)
        .addAction(title: "Not Sure", style: .default) 
            _ in
            print("No")
        
        .addAction(title: "YES", style: .default)  _ in
           print("Yes")
        
        .setPopoverPresentationProperties(sourceView: self, sourceRect: CGRect.init(x: 0, y: 0, width: 100, height: 100), barButtonItem: nil, permittedArrowDirections: .any)
        .build()
        .showAlert(animated: true)

【讨论】:

【参考方案4】:

首先找出窗口上最顶层的 viewController。

Get the top ViewController in iOS Swift

然后在该 viewController 上显示您的警报。无需传递任何参数。

public func alertUser(strTitle: String, strMessage: String) 
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
topmostVC().present(myAlert, animated: true, completion: nil)

【讨论】:

以上是关于如何显示来自不同班级的警报的主要内容,如果未能解决你的问题,请参考以下文章

如何使用来自两个不同表SQLite的元素更新/删除

何时需要获取PermanentIDsForObjects:error:?来自2个不同的班级?

我如何使用文本字段警报操作来更新单独的UIViewController上的标签?

依次显示两个相同的警报视图。如何区分来自警报 1 和警报 2 的文本?

如何在APEX SOQL查询中仅显示包含相同字段值的一条记录

如何在 blazor 组件中显示来自后端的自定义引导警报