Display UIAlertController from UIView/NSObject cla

2019-01-14 06:39发布

问题:

I have working iOS application In order to support iOS8, I am replacing UIAlertView/UIActionSheet with UIAlertController.

Problem :
For display UIAlertController I need presentViewController method of UIViewController class.
But UIAlertView is display from classes which are inherited from UIView or NSObject,
I can not get [self presentViewController...] method for obvious reason.

My Work :
I tried getting rootViewController form current window and display UIAlertController.

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

but have some rotation problems like if my current view controller do not have rotation support it will rotate if UIAlertController is open.

Question :
Did any one faced same problem and have safe solution ?
if yes please provide me some example or give some guide

回答1:

It looks like you are currently (pre-iOS8) triggering an alert view from within your view object. That's pretty bad practice, as in general alerts should be triggered from actions and logic. And that code should live in controllers.

I suggest you refactor your current code to move the logic that triggers the alert to the correct controller, and then you can easily upgrade to iOS 8 by using self as the controller.

If instead you're calling the alert from an outside object, then pass in the controller to the method that calls the alert. Somewhere upstream you must have knowledge of the controller.



回答2:

I solved an essentially similar problem today. Like Jageen, I ran into a situation where I wanted to present a UIAlertController but from a non-UIViewController class. In my case, I wanted an alert to pop up when the failure block of a HTTP request is run.

This is what I used and unlike our friend here, it worked quite perfectly for me.

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


回答3:

The better solution for UIView classes is below

ObjectiveC

UIViewController *currentTopVC = [self currentTopViewController];
currentTopVC.presentViewController......... 

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

Swift

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


回答4:

My solution is below:

Swift

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)
    }
}

Here is sample usage:

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


回答5:

I had a situation where a subview contains a button to dismiss it. I present an alert to confirm the action. It sends a message to the delegate - which is the view controller containing the subview - to remove the subview

Originally I presented a UIAlertView from a UIView. Refactoring for UIAlertController, since the UIAlertController can't present itself like a UIAlertView can, I came up with the following (in Swift; easily translated to ObjC):

Add a protocol to the subview:

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)

}

Add a delegate for the subview, and add a handler for the button/gesture tap:

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)
    }
}

Set the calling UIViewController as the delegate of the subview, e.g., in its viewDidLoad() method, and include protocol methods:

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()

       ...
    }

  ...
}

This avoids the need to find the topmost view controller, or pass references to view controllers to subviews (other than in an object/delegate relationship).



回答6:

In Swift 3:

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


回答7:

For Swift 4 and above

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


回答8:

I have had this problem. See my SO answer here for the code to get the topmost view controller with which to present another view controller.

I agree that for most cases it is bad practice to present a view controller from an object that is not a view controller, but sometimes you DO need to.



回答9:

For Display UIAlertController in NSObject Class use below Code.

    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];

// Put Below Method in Your Global Helper Class.

- (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];
}


回答10:

In general, alerts should be handled in the view controller. Here's an example of the code required:

Swift 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)
}