I have a main view controller and am presenting a child view. For one of the settings inside the child, I need to call the main view controller again. It would just make things a lot easier.
The following causes a hierarchy problem:
#define appDelegate (MyAppDelegate *)[[UIApplication sharedApplication] delegate]
[self presentViewController:[appDelegate mainViewController] animated:YES completion:^{}];
Is there any way to call the mainViewController easily while not losing the state of the child view?
EDIT
Here is how I'm presenting the child view.
ChildViewController *childViewController = [[ChildViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:childViewController];
[childViewController release];
[self presentModalViewController:navigationController animated:NO];
[navigationController release];
You can't present a view controller that has already been presented or is in a navigation stack. What is the mainViewController? Did you instantiate it? Has it been added to the screen yet?
If yes to both, you either need to back into it (dismiss to it) or remove it from it's parent first and then present it.
In your child view, you should have access to your application delegate and its mainViewController property, like this:
MyApplicationDelegate *appDelegate = (MyApplicationDelegate *)[[UIApplication sharedApplication] delegate];
Related
I've been receiving a warning that is related to my use of the GKTurnBasedMatchmakerViewController and the BannerViewController for iAd. That warning is:
Presenting view controllers on detached view controllers is discouraged <RootViewController: 0x14cd143c0>
What is wrong with the sequence of code shown below that causes this warning?
In AppDelegate.h
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *viewController;
}
In AppDelegate.m
- (void) applicationDidFinishLaunching:(UIApplication*)application {
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
[window setRootViewController:viewController];
[window addSubview: viewController.view];
}
Then when the user presses a Play button on my home screen, I first open the game center view controller as follows (notice the 2nd line is setting presentingViewController equal to the rootViewController that was set up in the appDelegate):
AppDelegate * theAppDelegate = (AppDelegate *) [UIApplication sharedApplication].delegate;
self.presentingViewController = theAppDelegate.viewController;
GKTurnBasedMatchmakerViewController *mmvc = [[GKTurnBasedMatchmakerViewController alloc] initWithMatchRequest:request];
[presentingViewController presentViewController: mmvc animated: YES completion:nil];
After the above code runs, a new scene is loaded. This scene is described in my GameSelectionLayer.h as follows:
#interface GameSelectionLayer : CCLayer <InAppStoreControlLayerDelegate> {
...
RootViewController *viewController;
AppDelegate *app;
BannerViewController *bannerViewController;
}
Then in my GameSelectionLayer.mm, I load the bannerViewController onEnter as follows:
-(void)onEnter {
[super onEnter];
app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
viewController = [(AppDelegate *)[[UIApplication sharedApplication] delegate] viewController];
bannerViewController = [[BannerViewController alloc] initWithContentViewController:viewController];
app.window.rootViewController = bannerViewController;
}
I receive the above mentioned warning when the above code runs. Please let me know what you think I might be doing wrong that is causing this warning.
The problem is you are replacing the view hierarchy of a view controller which has another view controller presented. The "detached" warning comes when you try to present a view controller from a view controller which is not attached to a window, or does not have a descendent view controller attached to a window.
Here I recommend two approaches. Either dismiss all view controllers before replacing the root view controller, or, the better of the two options, use another window with its own root view controller hierarchy.
Also, note that you should not add the view controller's view as a subview of the window. The system does it for you when you set the view controller as the root view controller.
I have two issues that I don't understand and am hoping that someone can help.
This code doesn't work for taking my existing UINavigationController hierarchy from a split view controller and taking over the screen with it. I just get a dark screen
UINavigationController* myself = self.navigationController;
[myself removeFromParentViewController];
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"QuestionnaireViewController"];
[myself pushViewController:controller animated:YES];
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
app.window.rootViewController = myself;
However, this code does work. Why can I set the rootViewController to a new UINavigationController but not self.navigationController?
UINavigationController *navController = [[UINavigationController alloc] init];
UINavigationController* myself = self.navigationController;
[myself removeFromParentViewController];
navController.viewControllers = myself.viewControllers;
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"QuestionnaireViewController"];
[navController pushViewController:controller animated:YES];
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
app.window.rootViewController = navController;
My second issue is in restoring the navigation controller to the splitViewController when the user goes "back". I know from experience that I can assign a new UINavigationController to the detailView, but I cannot assign self.navigationController.
I think the issues are the same issue. For some reason a new UINavigationController is not the same as a UIView's navigationController. Why?
Meddling with UIWindow is not very safe.
Depending on what you are trying to achieve, I can think of 2 rather simple options
a) iOS 5.1+ has an option to show/hide the RootViewController of your split and show with a swipe gesture, and work with that
b) Create your own UIViewController that emulates UISplitViewController, and hide the left part whenever you need to
I will try to explain this as best as I can.
My application has a TabBarController which functions as the main navigation
I have a modal view that I segue to to add a list. that screen can be reached from 2 different viewcontrollers.
From the main route I simple just close the modal and all is fine. However from the second route I need to be able to open up an entirely new ViewController.
The issue that I am having is that I can not seem to open that ViewController with the TabBar and NavBar included.
This is the code I am currently playing with to try to get it to work.
UITabBarController *tabController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainInterface"];
tabController.selectedIndex = 1;
//_window.rootViewController = tabController;
UINavigationController *groceryNavController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainNavController"];
UIViewController *groceryViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"GroceryViewController"];
UIViewController *currentVC = self;
[currentVC.navigationController pushViewController:groceryViewController animated:YES];
One way to to do it is through the delegate. If in the delegate, the relevant navigation controller is called:
self.navigationController
Then you would have to do:
YourAppDelegate *delegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.navigationController pushViewController:groceryViewController animated:YES];
(replace "YourAppDelegate" with the actual name of your app delegate)
I have a view controller subclass, SignInViewController, used for sign in that might be needed at any time. Rather than have every view controller in my app listen for the notification that sign in is needed, I'd rather have the app delegate do it.
But how do I trigger it from my app delegate?
Do I put the SignInViewController in my main storyboard? If so, how do I access my storyboard from my app delegate? Or is some other approach better?
You can always reference to your app delegate through the UIApplication singleton.
From there you can always get your root view controller.
With your root view controller you can get a reference to the storyboard.
Once you have your story board all you do is instantiate an instance of the view controller you want.
Present it.
AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
MainViewController *mvc = (MainViewController *)appDelegate.window.rootViewController;
LoginViewController *lvc = [mvc.storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[currentVC presentModalViewController:lvc animated:YES];
There may be a more direct way of getting a reference to your storyboard but this will almost always get it for you.
To show a view controller from anywhere (including the app delegate), I have used this code with success in iOS 8+ (I'm not sure about earlier compatibility). It will present from the modal view if there is one.
YOURAppDelegate *appDelegate = (YOURAppDelegate *)[[UIApplication sharedApplication] delegate];
UINavigationController *rootNavC = (UINavigationController *)appDelegate.window.rootViewController;
UIViewController *topVC = rootNavC.topViewController;
UIViewController *myNewVC = [rootNavC.storyboard instantiateViewControllerWithIdentifier:<YOUR STORYBOARD ID>];
if (topVC.presentedViewController)
{
if ([topVC.presentedViewController class] == [UINavigationController class])
{
dispatch_async(dispatch_get_main_queue(), ^{
[((UINavigationController*)topVC.presentedViewController) pushViewController:myNewVC
animated:YES];
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[topVC.presentedViewController.navigationController pushViewController:myNewVC
animated:animated];
});
}
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[rootNavC pushViewController:myNewVC
animated:animated];
});
}
I usually wrap this in a convenience method and pass in my instantiated view controller. To OP - you would create SignInViewController in the storyboard, assign it a unique storyboard ID, and then substitute that ID in the above code.
Is it possible to create multiple view or window in a (Window based) iPhone app?
Yes kind of possible. Just create a new view using a view controller and create an instance of that view in your class. Then in an ibaction you could do some removing and adding subviews. That's just a quick and easy way tho, you can get into a lot more detail with how you would manage each view, etc.
Edit on Request:
In your class, you would create an instance of it in the interface like so:
MyClass *myClass; (make sure to alloc and init in the init or awakeFromNib method)
Then make an instance of the app delegate in the ibaction like this:
MyAppDelegate *myAppDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
And then you can do this to switch from one view to another:
[self removeFromSuperView]; (or self.view in case this is a view controller)
[[myAppDelegate window] addSubview:myClass];
You can do something like the following to add a view programatically:
//If you create controllers via XCode, just link them in the .h file with IBOutlet
UIViewController *aViewController = [[UIViewController alloc] initWithNibName:#"YourNibName" bundle:[NSBundle mainBundle]];
self.viewController = aViewController;
[aViewController release];
// Add the view controller's view as a subview of the window
UIView *controllersView = [viewController view];
[window addSubview:controllersView];
[window makeKeyAndVisible];