Just started using Xcode 4.5 and I got this error in the console:
Warning: Attempt to present < finishViewController: 0x1e56e0a0 > on < ViewCont
With your main window, there will likely always be times with transitions that are incompatible with presenting an alert. In order to allow presenting alerts at any time in your application lifecycle, you should have a separate window to do the job.
/// independant window for alerts
@interface AlertWindow: UIWindow
+ (void)presentAlertWithTitle:(NSString *)title message:(NSString *)message;
@end
@implementation AlertWindow
+ (AlertWindow *)sharedInstance
{
static AlertWindow *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[AlertWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
});
return sharedInstance;
}
+ (void)presentAlertWithTitle:(NSString *)title message:(NSString *)message
{
// Using a separate window to solve "Warning: Attempt to present <UIAlertController> on <UIViewController> whose view is not in the window hierarchy!"
UIWindow *shared = AlertWindow.sharedInstance;
shared.userInteractionEnabled = YES;
UIViewController *root = shared.rootViewController;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
alert.modalInPopover = true;
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
shared.userInteractionEnabled = NO;
[root dismissViewControllerAnimated:YES completion:nil];
}]];
[root presentViewController:alert animated:YES completion:nil];
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
self.userInteractionEnabled = NO;
self.windowLevel = CGFLOAT_MAX;
self.backgroundColor = UIColor.clearColor;
self.hidden = NO;
self.rootViewController = UIViewController.new;
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(bringWindowToTop:)
name:UIWindowDidBecomeVisibleNotification
object:nil];
return self;
}
/// Bring AlertWindow to top when another window is being shown.
- (void)bringWindowToTop:(NSNotification *)notification {
if (![notification.object isKindOfClass:[AlertWindow class]]) {
self.hidden = YES;
self.hidden = NO;
}
}
@end
Basic usage that, by design, will always succeed:
[AlertWindow presentAlertWithTitle:@"My title" message:@"My message"];
Where are you calling this method from? I had an issue where I was attempting to present a modal view controller within the viewDidLoad
method. The solution for me was to move this call to the viewDidAppear:
method.
My presumption is that the view controller's view is not in the window's view hierarchy at the point that it has been loaded (when the viewDidLoad
message is sent), but it is in the window hierarchy after it has been presented (when the viewDidAppear:
message is sent).
Caution
If you do make a call to presentViewController:animated:completion:
in the viewDidAppear:
you may run into an issue whereby the modal view controller is always being presented whenever the view controller's view appears (which makes sense!) and so the modal view controller being presented will never go away...
Maybe this isn't the best place to present the modal view controller, or perhaps some additional state needs to be kept which allows the presenting view controller to decide whether or not it should present the modal view controller immediately.
I've ended up with such a code that finally works to me (Swift), considering you want to display some viewController
from virtually anywhere. This code will obviously crash when there is no rootViewController available, that's the open ending. It also does not include usually required switch to UI thread using
dispatch_sync(dispatch_get_main_queue(), {
guard !NSBundle.mainBundle().bundlePath.hasSuffix(".appex") else {
return; // skip operation when embedded to App Extension
}
if let delegate = UIApplication.sharedApplication().delegate {
delegate.window!!.rootViewController?.presentViewController(viewController, animated: true, completion: { () -> Void in
// optional completion code
})
}
}
I just had this issue too, but it had nothing to do with the timing. I was using a singleton to handle scenes, and I set it as the presenter. In other words "Self" wasn't hooked up to anything. I just made its inner "scene" the new presenter and voila, it worked. (Voila loses its touch after you learn its meaning, heh).
So yeah, it's not about "magically finding the right way", it's about understanding where your code stands and what it's doing. I'm happy Apple gave such a plain-English warning message, even with emotion to it. Kudos to the apple dev who did that!!
I found this bug arrived after updating Xcode, I believe to Swift 5. The problem was happening when I programatically launched a segue directly after unwinding a view controller.
The solution arrived while fixing a related bug, which is that the user was now able to unwind segues by swiping down the page. This broke the logic of my program.
It was fixed by changing the Presentation mode on all the view controllers from Automatic to Full Screen.
You can do it in the attributes panel in interface builder. Or see this answer for how to do it programatically.
Have to write below line.
self.searchController.definesPresentationContext = true
instead of
self.definesPresentationContext = true
in UIViewController