Attempt to present * on * whose view is not in the window hierarchy - objective-c

I'm trying to make a modal view controller in my app delegate (I created a function called showLoginView). But whenever I try to call it I get a warning in XCode:
Warning: Attempt to present <PSLoginViewController: 0x1fda2b40> on <PSViewController: 0x1fda0720> whose view is not in the window hierarchy!
Here's the method code:
- (void)showLoginView
{
PSLoginViewController *loginViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:#"PSLoginViewController"];
[self.window.rootViewController presentViewController:loginViewController animated:NO completion:nil];
}
How can I add the view to the window hierarchy? Or maybe I'm doing something very wrong?

You can't display a modal view controller from the appDelegate. You need to display a modal ViewController from whichever viewController is currently displaying full-screen. In other words, you need to put that code into your root view controller, or whichever one you want to display the modal vc from...
Also, you'll want to use the method "presentModalViewController" to present the modal. You can set properties on the modal vc such as:
vC.modalPresentationStyle = UIModalPresentationFormSheet;
vC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:vC animated:YES];

You can actually present a modal view Controller from the AppDelegate as long as you detect the current visible viewController and take care of the case where you current controller is a navigationController.
Here is what I do:
UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
activeController = [(UINavigationController*) activeController visibleViewController];
}
[activeController presentModalViewController:loginViewController animated:YES];

UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]])
{
activeController = [(UINavigationController*) activeController visibleViewController];
}
else if (activeController.modalViewController)
{
activeController = activeController.modalViewController;
}
[activeController presentModalViewController:vc animated:YES];

I ran into this problem on iOS 7 - the key to making any of the proposed solutions work was to call
[self.window makeKeyAndVisible];
in your AppDelegate.
After that call, presenting a modal view from the window's rootViewController worked.

Another reason for that warning can be that you want to present a view controller from an instance which is not the top most view controller.
So first you have to get the topmost UIViewController and using this instance to call presentViewController:
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
while (root.presentedViewController) {
root = root.presentedViewController;
}

You can NSLog(#"%#", self.window.rootViewController), and see what the rootViewController really is.
I came into this problem, when the rootViewController is a normal UIViewController.
Replace it with a UINavigationController, wish it will help.

Faced this issue while trying to present controller from the call of delegate of other controller . i.e : show search filter with delegate , once done back to my controller and receive data via the delegate then present controller , all I had to do is to dispatch the present code cause while in a delegate you're in another thread , that's why you're presenting on your view from main thread another controller from that other thread , so have to go back to main thread , just put the presenting code like this :
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:searchVC animated:true completion:nil];
});
Hope this helps !

Related

EXC_BAD_ACCESS EXC_I386_GPFLT while click on button

i have a UIViewController with UITableView, when the tableView is empty i want to show another view so i am using this
[self.tableView setHidden:YES];
NoKidsViewController *noKids = [self.storyboard instantiateViewControllerWithIdentifier:#"NoKidsView"];
[self.view addSubview:noKids.view];
all is fine, i'm able to see the view. but when i tap on one of the buttons in it i'm getting the EXC_BAD_ACCESS EXC_I386_GPFLT error.
//NoKidsViewController
- (IBAction)addNewKid:(id)sender {
AddKid *addKidController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddKid"];
[self.navigationController pushViewController:addKidController animated:YES];
}
- (IBAction)saleSpot:(id)sender {
SaleSpot *saleSpotController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddKid"];
[self.navigationController pushViewController:saleSpotController animated:YES];
}
i searched the net over 3 hours trying to find any solution w/o success. what could cause that error? and how can i fix it?
The noKids controller is going out of scope and being deallocated. This is what is often referred to as a zombie object.
You need to add the noKids controller to the childViewControllers of the containing controller.
NoKidsViewController *noKids = [self.storyboard instantiateViewControllerWithIdentifier:#"NoKidsView"];
[self addChildViewController:noKids];
[self.view addSubview:noKids.view];
[noKids didMoveToParentViewController:self];
This will retain the NoKidsViewController as well as allow the view controller methods to pass down to it. For more information on creating your custom container view controller:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

Wrong 'presentingViewController'

In my MainViewController, I present another view controller through this:
MessageViewController *messageController = [[MessageViewController alloc]initWithNibName:nil bundle:nil];
[messageController setModalPresentationStyle:UIModalPresentationFullScreen];
[messageController setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:messageController animated:YES completion:nil];
[messageController release];
This will display the view controller correctly. However, when I try to go back to the presenting view controller, which in this case should be the MainViewController, this code doesn't work:
if ([self.presentingViewController isKindOfClass:[MainViewController class]])
[(MainViewController *)self.presentingViewController setCurrentViewTag:2];
[self dismissModalViewControllerAnimated:YES];
I removed the "if.." condition to force it in setting the current view tag. An error occurred telling me that the presenting view controller seems to be the UINavigationController:
[UINavigationController setCurrentViewTag:]: unrecognized selector sent to instance 0x8352a50
Can anyone tell me why is this happening? This code used to work before and I am not sure what changed to make it stop working properly.
EDIT
Here is the updated code:
ReaderController *readerController = [[ReaderController alloc]initWithNibName:nil bundle:nil];
[readerController loadWhichViewToShow:2];
[self setDefinesPresentationContext:YES];
[readerController setModalPresentationStyle:UIModalPresentationFullScreen];
[readerController setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:readerController animated:YES completion:nil];
[readerController release];
Calling [self presentViewController:messageController animated:YES completion:nil]; doesn't necessarily use the vc you call this on to present the other vc. By default it travels up the vc-hierarchy and presents the other vc on the root view controller. That's why in your case the presenting view controller is a UINavigationController.
If you want to force your MainViewController to be the presenting vc, you have call:
[self setDefinesPresentationContext:YES];
on your MainViewController before presenting the MessageViewController.
Edit: In case someone else reads this: definesPresentationContext seems to be bugged or the documentation is wrong. See the comments below and Cocoa Builder
copy of my answer from this question
from Programming iOS 6, by Matt Neuburg:
On the iPad, when the presented view controller’s modalPresentationStyle is UIModalPresentationCurrentContext, a decision has to be made as to what view controller should be the presented view controller’s presentingViewController. This will determine what view will be replaced by the presented view controller’s view. This decision involves another UIViewController property, definesPresentationContext (a BOOL). Starting with the view controller to which presentViewController:animated:completion: was sent, we walk up the chain of parent view controllers, looking for one whose definesPresentationContext property is YES. If we find one, that’s the one; it will be the presentingViewController, and its view will be replaced by the presented view controller’s view. If we don’t find one, things work as if the presented view controller’s modalPresentationStyle had been UIModalPresentationFullScreen.
TL;DR
1. set definesPresentationContext to true on the desired presentingViewController
2. set modalPresentationStyle to UIModalPresentationCurrentContext on the desired presentedViewController
If seems that you need to set three thing in iOS 11.
controller.modalPresentationStyle = UIModalPresentationCurrentContext;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
self.definesPresentationContext = YES;
[self presentViewController:controller animated:YES completion:nil];

Going back to first ViewController from second ViewController

I'm building an app that currently has 3 ViewControllers. One of them is used after a successful login so is not relevant in this question.
I'm using a mixture of Storyboards and building things programmatically when I find Storyboards do not give me the fine control that I need.
The first ViewController is built in my 'MainStoryboard'. It has a login form and an info button at the bottom. I link it up the my AppDelegate by doing the following inside didFinishLaunchingWithOptions:
ViewController *viewController = (ViewController *)self.window.rootViewController;
Because I wanted to force rendering of a UIWebView (another story) I create the second view programmatically. I do the following inside didFinishLaunchingWithOptions:
infoViewController = [[InfoViewController alloc] init];
[infoViewController view];
Inside both of my ViewControllers I setup a link to appDelegate as below:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
I have an info button in my first ViewController that takes you to the infoViewController. It calls the following code when tapped:
appDelegate.infoViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:appDelegate.infoViewController animated:YES];
The above works just fine for me, flips over the screen and shows the InfoViewController.
On my InfoViewController I have a button that should take you back to the login page, I have tried all sorts to get this to work but it just crashes my app. Nothing seems to work. I have tried the following:
appDelegate.viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:appDelegate.viewController animated:YES];
and
[self.navigationController popToRootViewControllerAnimated:YES];
and
[self.navigationController popViewControllerAnimated:YES];
and
[self.navigationController popToViewController:appDelegate.viewController animated:YES];
I suspect the last 3 might be more to do with when you have a navigation view controller and you want to go back to the root? I'm not sure, but either way it does not work. I had this working using storyboards previously so I'm sure it ought to be easy! As mentioned I switched to making the infoViewController programmatically so that I could force the UIWebView to render before the view appeared.
Any help much appreciated.
You can do with:
[self dismissModalViewControllerAnimated:YES];
You should use this.
[self dismissModalViewControllerAnimated:YES];
You should use a main controller for switching between your other view controllers. Change the view of your root controller to one of your other view controllers (apply animations as usual if needed). Hold a pointer to your root controller in your other view controllers and call self.rootController.view = <desired_controller_instance>.view
I think the way you're presenting your InfoViewController is wrong. Do it the following way:
In your ViewController, create an action for the info button.:
- (IBAction)infoButtonTapped:(id)sender
{
InfoViewController *infoViewController = [[InfoViewController alloc] init];
infoViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:infoViewController animated:YES];
}
And in your InfoViewController, in the action of your button that should take you back write this:
- (void)takeBackToViewController
{
[self dismissModalViewControllerAnimated:YES];
}
Hope it works.
Also in presented controller you can use this
if(self.parentViewController)
[self.parentViewController dismissModalViewControllerAnimated:YES];
else
[self.presentingViewController dismissModalViewControllerAnimated:YES];
To dissmiss current controller.

load new viewcontroller in method

Im new in objective c.
I have this method, and i want it to load a new view, in the end, from my storyboard. How can i do that?
- (void)stopAnimatingAndLoadView {
[self.indicator stopAnimating];
// load new view
}
Hope you understand my question
The new view that you want to load, I assume, is a UIViewController and you have already set it up in storyboard. In storyboard, if you give it an identifier, then you can load it by calling:
MyOtherViewController *myOtherViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MyOtherViewController"];
// If you are using navigation controller, you can call
[self.navigationController pushViewController:myOtherViewController animated:YES];
// If not, you can present it modally
[self presentModalViewController:myOtherViewController animated:YES];

Presenting a Modal View Controller hides the Navigation Bar

I have a navigation based app with a navigation bar, but there are a few instances where instead of pushing a view controller onto the stack, I need to present the view controller modally. The problem is that when I dismiss the modal view controller, everything functions as expected except that the navigation bar is hidden and the (parent view) has been resized, which is the expected behavior according to the docs. So I figured I could simply call a built-in method to unhide the navigation bar. I have already tried
[self.navigationController setNavigationBarHidden:NO];
as well as the animated version without success.
The documentation talks about this in the method
presentModalViewController: animated:
in the discussion section where it says,
On iPhone and iPod touch devices, the view of modalViewController is always presented full screen" and "Sets the modalViewController property to the specified view controller. Resizes its view and attaches it to the view hierarchy."However, the docs didn't clue me in as to how to undo this process after dismissing a modal view.
Has anyone else experienced this and found a solution?
Edit: I am having this same problem, so instead of asking my own question I am sponsoring a bounty on this one. This is my specific situation:
In my case, I am presenting an Image Picker in a Modal View Controller, over a Navigation Controller:
-(void) chooseImage {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
imagepicker = [[UIImagePickerController alloc] init];
imagepicker.allowsEditing = NO;
imagepicker.delegate = self;
imagepicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagepicker.navigationBar.opaque = true;
imagepicker.wantsFullScreenLayout = NO;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if (self.view.window != nil) {
popoverController = [[UIPopoverController alloc] initWithContentViewController:imagepicker];
[popoverController presentPopoverFromBarButtonItem:reset permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
} else {}
} else {
[self.navigationController presentModalViewController:imagepicker animated:YES];
}
}
}
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.popoverController dismissPopoverAnimated:true];
} else {
[self.navigationController dismissModalViewControllerAnimated:YES];
}
//Save the image
}
-(void) imagePickerControllerDidCancel:(UIImagePickerController *)picker {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.popoverController dismissPopoverAnimated:true];
} else {
[self.navigationController dismissModalViewControllerAnimated:YES];
}
}
Make sure you a presenting AND dismissing the modalViewController from the UINavigationController, like so:
// show
[self.navigationController presentModalViewController:vc animated:YES];
// dismiss
[self.navigationController dismissModalViewControllerAnimated:YES];
If your view controller is actually on the UINavigationController's stack then this is the correct way to handle the presentation and dismissal of the modal view controller. If your UINavigationBar is still hidden, there is something else funky going on and we would need to see your code to determine what is happening.
Edit
I copied your code into an app of mine and the UIImagePickerController successfully presented and dismissed and my UINavigationController's UINavigationBar was still there. I truly believe that the problem lays elsewhere in your architecture. If you upload a zip w/ an example project I will take a look.
Simply try following code it will work
SettingsViewController *settings = [[SettingsViewController alloc] init];
UINavigationController *navcont = [[UINavigationController alloc] initWithRootViewController:settings];
[self presentModalViewController:navcont animated:YES];
[settings release];
[navcont release];
One need to present the navigation controller in order to have navigation bar on the presented controller
I think I've seen this behavior when presenting a view controller on the wrong VC. Are you calling presentModalViewController on the navigation controller or the individual VC?
Try calling it from the navigationController if you aren't already.
[self.navigationController presentModalViewController:myVC animated:YES];
If you present a controller as model, View controller will appear to total view.
If you want to access the navigation controller properties over the model view, You need to create another navigation controller reference and it continues as previous.
This may be useful for you.
Check this out. This is Apple's Documentation under UIViewController Class Reference:
It clearly mentions that modal view always presents in full screen mode, so it is obvious that navigation bar will be hidden. So put the seperate navigation bar on modal view to navigate back.
presentModalViewController:animated:
Presents a modal view managed by the given view controller to the user.
- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
Parameters
modalViewController
The view controller that manages the modal view.
animated
If YES, animates the view as it’s presented; otherwise, does not.
Discussion
On iPhone and iPod touch devices, the view of modalViewController is always presented full screen. On iPad, the presentation depends on the value in the modalPresentationStyle property.
Sets the modalViewController property to the specified view controller. Resizes its view and attaches it to the view hierarchy. The view is animated according to the transition style specified in the modalTransitionStyle property of the controller in the modalViewController parameter.
Availability
Available in iOS 2.0 and later.
Hope this helps you understand that hiding the whole view along with navigation controller is default behaviour for modal view so try putting a seperate navigation bar in modal view to navigate.
You can check it further on this link
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
AddContactVC *addController =[self.storyboard instantiateViewControllerWithIdentifier:#"AddContactVC"];
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:addController];
[self presentViewController:navigationController animated:YES completion: nil];
working for me shows navigation bar
Emphatic and Devin –
As I started reading through the Apple docs to get familiar with the problem, I noticed that the method you're using, presentModalViewController:animated:, appears to be deprecated in favor of presentViewController:animated:completion:. Perhaps you should try to use that method instead.
For your convenience, take a look for yourself:
presentModalViewController:animated: reference
I'll try to put together a quick test program to see whether what I've said above is actually true. But give it a shot – maybe it'll help!
Xcode has a template that does pretty close to what you're doing. from the results, i don't think you should be attempting to perform [self.navigationController presentModalViewController:vc] and [self.navigationController dismissModalViewControllerAnimated:] , but rather simply [self presentModalViewController:] and [self dismissModalViewControllerAnimated:] .
to see how the template does this for yourself, you can use the new project wizard in xcode 4.3 . perhaps it will provide some guidance:
from that choice, choose Next, then give your test project a name, choose "Universal", turn off automatic reference counting, hit next, save where you want it.
now, click on the target and switch the deployment target to 4.3 (or 4.0 if you prefer) for your testing purposes, and switch to your device or the iOS 4.3 simulator .
finally, substitute the following code in applicationDidFinishLaunching:withOptions: in the created AppDelegate.m:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.mainViewController = [[[MainViewController alloc] initWithNibName:#"MainViewController_iPhone"
bundle:nil] autorelease];
} else {
self.mainViewController = [[[MainViewController alloc] initWithNibName:#"MainViewController_iPad"
bundle:nil] autorelease];
}
UINavigationController* navigationController
= [[UINavigationController alloc] initWithRootViewController:self.mainViewController];
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
now, when i run this, it doesn't hide the navigationBar. and in the created MainViewController.m from the template, you'll see how it presents the modal view controller and dismisses it from the controller itself and not from the navigation controller. for good measure, to make the template code more like your own, go into MainViewController.m and delete the line that sets the modal view controller transition style ...
(of course, in iOS 5, with storyboards, the same thing can all be accomplished with modal segues ... which is how i've done this for apps that i'm not supporting for pre-5.0 that present a modalViewController in this fashion.)
One of the best solution it to use this Category MaryPopin
https://github.com/Backelite/MaryPopin