Reputation: 155
I'm trying to call function from one class in view controller:
var rate = RateMyApp.sharedInstance
rate.showRatingAlert()
and in RateMyApp class I call alert view:
var window: AnyObject = UIApplication.sharedApplication().keyWindow!;
var vc = window.rootViewController;
vc?!.presentViewController(alert, animated: true, completion: nil)
However, the alert is not shown and there is a kind of warning:
Warning: Attempt to present <UIAlertController: 0x15fe3dcf0> on <drawtext.ViewController: 0x16002a800> whose view is not in the window hierarchy!
That happens from time to time, if one hides the app and then opens it again several times, the alert sometimes is shown.
What can cause such strange behavior?
Upvotes: 0
Views: 782
Reputation: 1980
While using UIAlertController it should be presented form the topmost ViewController in the view hierarchy.
so i suggest:
var rate = RateMyApp.sharedInstance
rate.showRatingAlert(self)
In RateMyApp class:
func showRatingAlert(sender:UIViewController){
//..... your UIAlertController here
sender.presentViewController(alert, animated: true, completion: nil)
}
On a more general note, the func presentViewController(:_)
should be called only from the top-most View Controller.
You can create an extension for it like so:
extension UIViewController {
@warn_unused_result
static func topViewController(base: UIViewController? = UIApplication.sharedApplication().windows.first?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController
if let top = moreNavigationController.topViewController where top.view.window != nil {
return topViewController(top)
} else if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
if let splitVC = base as? UISplitViewController {
if let lastVC = splitVC.viewControllers.last {
return topViewController(lastVC)
}
else {
return splitVC
}
}
if let lastChildVC = base?.childViewControllers.last {
return topViewController(lastChildVC)
}
return base
}
}
You can call the alert as follows:
func showRatingAlert() {
if let vc = UIViewController.topViewController() {
vc.presentViewController(alert, animated: true, completion: nil)
}
}
Upvotes: 1
Reputation: 155
Seems to be solved:
var window = UIApplication.sharedApplication().keyWindow!
// var vc = window.rootViewController!!.presentingViewController
var vc = window.rootViewController;
while (vc!.presentedViewController != nil)
{
vc = vc!.presentedViewController;
}
vc?.presentViewController(alert, animated: true, completion: nil)
looks like Chetan's solution
Upvotes: 1
Reputation: 2225
Do it when the window is up
dispatch_async(dispatch_get_main_queue(), {
var window: AnyObject = UIApplication.sharedApplication().keyWindow!;
var vc = window.rootViewController;
vc?!.presentViewController(alert, animated: true, completion: nil)
})
Upvotes: 0
Reputation: 2297
As per my thought you just need
Get top most viewcontroller [currently presented]
Present your alert.
To Get Topmost View Controller
[This is obj C code. Please make efforts to convert in swift]
-(UIViewController*)topViewController {
return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
-(UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
} else if (rootViewController.presentedViewController) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
} else {
return rootViewController;
}
}
Then
UIViewController *topController = [self topViewController];
Now present your alert on topController.
Sorry i can't deliver you Swift Code. But Hope this is enough to hint.
Upvotes: 0