26

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

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

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

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

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

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

4

10 回答 10

44

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

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

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(errorAlert, animated: true, completion: nil)
于 2015-01-05T06:37:04.853 回答
15

类的更好解决方案UIView如下

客观C

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........
于 2015-07-08T09:54:44.580 回答
13

我的解决方案如下:

迅速

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!")
于 2015-11-05T18:07:10.480 回答
10

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

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

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

于 2014-08-26T12:15:50.690 回答
6

对于斯威夫特 4

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

对于斯威夫特 5

UIApplication.shared.windows.last?.rootViewController?.present(alert, animated: true)
于 2018-07-13T16:13:45.050 回答
4

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

最初我从一个 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()

       ...
    }

  ...
}

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

于 2015-02-08T21:12:19.803 回答
3

在斯威夫特 3 中:

UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
于 2016-11-25T12:46:56.717 回答
0

对于在 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];
}
于 2017-08-24T09:34:57.580 回答
0

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

斯威夫特 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)
}
于 2017-08-24T09:51:52.620 回答
0

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

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

    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
}
于 2019-04-02T11:12:04.140 回答