【问题标题】:Display UIAlertController from UIView/NSObject class从 UIView/NSObject 类显示 UIAlertController
【发布时间】:2014-10-19 17:35:37
【问题描述】:

我有 working iOS application 为了support iOS8,我换成了UIAlertView/UIActionSheet with UIAlertController

问题:
对于显示 UIAlertController 我需要 presentViewController UIViewController 类的方法。
但是 UIAlertView 显示来自 inherited 的类 UIView or NSObject,
我无法获得[self presentViewController...] 方法,原因很明显。

我的工作:
我尝试从当前窗口获取 rootViewController 并显示 UIAlertController。

[[[UIApplication sharedApplication] keyWindow].rootViewController presentViewController ...]

但是有一些旋转问题,比如我当前的视图控制器不支持旋转 如果 UIAlertController 打开,它将旋转。

问题:
有没有人遇到过同样的问题并有安全的解决方案?
如果是,请给我一些例子或提供一些指导

【问题讨论】:

  • 我遇到过这个问题。请参阅我的SO answer 此处获取用于呈现另一个视图控制器的最顶层视图控制器的代码。我同意在大多数情况下,从不是视图控制器的对象中呈现视图控制器是不好的做法,但有时您确实需要。

标签: objective-c uialertview ios8 uiactionsheet uialertcontroller


【解决方案1】:

看起来您当前(iOS8 之前)正在从您的视图对象中触发警报视图。这是非常糟糕的做法,因为通常警报应该从动作和逻辑触发。并且该代码应该存在于控制器中。

我建议你重构你当前的代码,将触发警报的逻辑移动到正确的控制器,然后你可以通过使用self作为控制器轻松升级到iOS 8。

如果您是从外部对象调用警报,则将控制器传递给调用警报的方法。在上游的某个地方,您必须了解控制器。

【讨论】:

  • +1 建议,你是对的,调用 UIAlertView 的地方是错误的,但是,在当前状态下我无法重构它,我在很多地方发现了同样的问题,等待修改较少的建议,任何谢谢,如果没有其他建议,将应用您的逻辑。
  • 以前,UIAlertView 一直在任何UIViewNSObject 工作。例如:显示来自UITableViewCell 的警报有效。无需从UIViewController 实例中显示它。现在,UIAlertController 只呈现来自UIViewController,这不容易显示来自单元格的警报。
  • “一般来说,警报应该由动作和逻辑触发。并且该代码应该存在于控制器中。” - 我更喜欢在我的模型中将我的业务逻辑远离(视图)控制器。控制器应该只处理他们控制的视图的视觉方面。所以这一切都是没有意义的。
  • @Jonny 业务逻辑存在于业务类中,但显示逻辑存在于显示控制器中。当显示控制器调用返回错误的业务逻辑时,由它决定如何将其显示给用户。我希望这能让它更清楚。
【解决方案2】:

我今天解决了一个基本类似的问题。像Jageen 一样,我遇到了一种情况,我想展示一个 UIAlertController 但来自一个非 UIViewController 类。就我而言,我希望在运行 HTTP 请求的失败块时弹出警报。

这是我使用的,与我们这里的朋友不同,它对我来说非常完美。

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(errorAlert, animated: true, completion: nil)

【讨论】:

  • 谢谢。但是,您如何看待 Rikkles 上面所说的话?你的范式真的被打破了吗?
  • rootViewController 已经在展示其他内容时,这将不起作用。
  • 这对我来说是无操作的。
【解决方案3】:

我遇到过一个子视图包含一个关闭它的按钮的情况。我发出警报以确认操作。它向代表(包含子视图的视图控制器)发送消息以删除子视图

最初我从一个 UIView 展示了一个 UIAlertView。重构 UIAlertController,因为 UIAlertController 不能像 UIAlertView 那样呈现自己,我想出了以下内容(在 Swift 中;很容易翻译为 ObjC):

向子视图添加协议:

protocol MySubviewDelegate {

    // called when user taps subview/delete button
    //   or, you could call it from a gesture handler, etc.
    func displayAlert(alert : UIAlertController)

    // called when user confirms delete from the alert controller
    func shouldRemoveSubview(sender : AnyObject)

}

为子视图添加一个委托,并为按钮/手势点击添加一个处理程序:

class MySubview : UIView {

    var subviewDelegate : MySubviewDelegate!

    ...

    func handleTap(sender : AnyObject) {

        // set up the alert controller here
        var alert = UIAlertController(title: "Confirm Delete", 
            message: "This action is permanent. Do you wish to continue?", 
            preferredStyle: UIAlertControllerStyle.Alert)

        // Cancel action 
        //   nil handler means "no action if Cancel button selected"
        alert.addAction(UIAlertAction(title: "Cancel",
            style: UIAlertActionStyle.Cancel,
            handler: nil))

        // Confirm action
        alert.addAction(UIAlertAction(title: "Confirm",
            style: UIAlertActionStyle.Default,
            handler: { (action : UIAlertAction!) -> Void in

                // call delegate method to perform confirmed action, - i.e. remove
                self.subviewDelegate.shouldRemoveSubview(self)
        }))

        // call delegate method to display alert controller
        //   send alert object to delegate
        self.subviewDelegate.displayAlert(alert)
    }
}

将调用 UIViewController 设置为子视图的委托,例如,在其 viewDidLoad() 方法中,并包含协议方法:

class viewController : UIViewController, MySubviewDelegate {

    override func viewDidLoad() {

        super.viewDidLoad()

        self.subviewDelegate = self

        ...
    }

    func displayAlert(alert : UIAlertController) {

        presentViewController(alert, animated: true, completion: nil)
    }

    func shouldRemoveSubview(sender : AnyObject) {

        // cast as UIView / MySubview subclass
        var subview = sender as MySubview

       // remove the subview / perform the desired action
       subview.removeFromSuperview()

       ...
    }

  ...
}

这避免了查找最顶层视图控制器的需要,或将视图控制器的引用传递给子视图(对象/委托关系除外)。

【讨论】:

    【解决方案4】:

    UIView 类的更好解决方案如下

    ObjectiveC

    UIViewController *currentTopVC = [self currentTopViewController];
    currentTopVC.presentViewController......... 
    
    - (UIViewController *)currentTopViewController
    {
        UIViewController *topVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
        while (topVC.presentedViewController)
        {
            topVC = topVC.presentedViewController;
        }
        return topVC;
    }
    

    斯威夫特

    var topVC = UIApplication.sharedApplication().keyWindow?.rootViewController
    while((topVC!.presentedViewController) != nil){
         topVC = topVC!.presentedViewController
    }
    topVC?.presentViewController........
    

    【讨论】:

    • 这很棒 - 但我发现我可以更简单地做到这一点:在我的 AlertViewController 中,我只是添加了以下代码:UIViewController *topVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; [topVC presentViewController:presentUpdatedDataAvailableAlert animated:YES completion:nil];
    【解决方案5】:

    我的解决方案如下:

    斯威夫特

    class alert {
        func msg(message: String, title: String = "")
        {
            let alertView = UIAlertController(title: title, message: message, preferredStyle: .Alert)
    
            alertView.addAction(UIAlertAction(title: "Done", style: .Default, handler: nil))
    
            UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertView, animated: true, completion: nil)
        }
    }
    

    这里是示例用法:

    let Alert = alert()
    Alert.msg("My alert (without title)")
    Alert.msg("This is my alert", title: "Warning!")
    

    【讨论】:

      【解决方案6】:

      在 Swift 3 中:

      UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
      

      【讨论】:

        【解决方案7】:

        对于在 NSObject 类中显示 UIAlertController 使用下面的代码。

            UIAlertController * popup =   [UIAlertController
                                      alertControllerWithTitle:nil
                                      message:nil
                                      preferredStyle:UIAlertControllerStyleActionSheet];
        
            UIAlertAction* cancel = [UIAlertAction
                                     actionWithTitle:@"Cancel"
                                     style:UIAlertActionStyleCancel
                                     handler:^(UIAlertAction * action) {
                                         [popup dismissViewControllerAnimated:YES completion:nil];
                                     }];
            [popup addAction:cancel];
        
            UIViewController *rootViewController = [[Helper shareInstance] topViewController];
            [rootViewController presentViewController:popup animated:YES completion:nil];
        

        // 把下面的方法放在你的全局助手类中。

        - (UIViewController *)topViewController {
          return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
        }
        
        - (UIViewController *)topViewController:(UIViewController *)rootViewController {
            if (rootViewController.presentedViewController == nil) {
                return rootViewController;
            }
        
            if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
                UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
                UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
                return [self topViewController:lastViewController];
            }
        
            UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
            return [self topViewController:presentedViewController];
        }
        

        【讨论】:

          【解决方案8】:

          一般来说,警报应该在视图控制器中处理。这是所需代码的示例:

          斯威夫特 3

          private func displayError(message: String) {
              let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
              let okayAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
              alertController.addAction(okayAction)
              present(alertController, animated: true, completion: nil)
          }
          

          【讨论】:

            【解决方案9】:

            对于 Swift 4

            UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
            

            对于 Swift 5

            UIApplication.shared.windows.last?.rootViewController?.present(alert, animated: true)
            

            【讨论】:

              【解决方案10】:

              我知道这个问题已经得到解答......但是我也在寻找同样的问题,但上述解决方案都没有为我工作。

              所以经过多次尝试和错误,我最终找到了一个非常简单且可持续的解决方案。

                  func showError(title: String?, error: String?) {
              
                  DispatchQueue.main.async(execute: {
              
                      let alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.alert)
              
                      alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
              
                      CommonMethods.instance.topMostController()?.present(alert, animated: true, completion: nil)
              
                  })
              }
              
              static let instance = CommonMethods()
              
              fileprivate func topMostController() -> UIViewController? {
              
                  var presentedVC = UIApplication.shared.keyWindow?.rootViewController
                  while let pVC = presentedVC?.presentedViewController {
                      presentedVC = pVC
                  }
              
                  if presentedVC == nil {  }
                  return presentedVC
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-10-10
                • 2016-01-09
                • 1970-01-01
                • 2016-07-09
                • 1970-01-01
                相关资源
                最近更新 更多