performing segue from appdelegate - objective-c

I have the following code:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
//make sure that the user credentials are still ok
if (userLeftApplication){
BaseViewController * baseViewController = [[BaseViewController alloc]init];
BOOL detailsAreOK = [baseViewController credentialsValidated];
if (!detailsAreOK){
[self.window.rootViewController performSegueWithIdentifier: #"fromSplashToLogin" sender: self.window.rootViewController];
}
userLeftApplication = FALSE;
}
}
However, I get the following exception when trying to perform the segue:
Attempt to present <LoginViewController: 0x2012e180> on <FirstViewController: 0x1f59cef0> whose view is not in the window hierarchy!
and the user is not being directed there.
What is wrong?

rootViewController isn't currently defined. You can't 'perform a segue from the App Delegate', segues are transitions between view controllers. You need to launch the view controller rather than perform a segue.
self.window.rootViewController = baseViewController;

Related

Set a new window.rootViewController before the view gets loaded

So I am trying to install this Navigation Framework in my app:
https://github.com/weissi/FRLayeredNavigationController
http://www.youtube.com/watch?v=k9bFAYtoenw&feature=plcp
Now, if you look at the attached image, I have my login screen. Once the login is done, I do a Segue Modal push into my "home" page and in there, I want to start having the FRLayeredNavigationController once I reach my homepage. Is that possible while using the storyboard? According to the Youtube Video, one would usually use the FRLayeredNavigationController by doing:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
HomeViewController* homeController = [[HomeViewController alloc] init];
FRLayeredNavigationController* lnc = [[FRLayeredNavigationController alloc] initWithRootViewController:homeController];
self.window.rootViewController = lnc;
}
[self.layeredNavigationController pushViewController:vc inFrontof:self maximumWidth:NO animated:YES];
I have not fount a way to do this using Segue's... But the way I have accomplish this is as follows:
Providing the login was successful and you are wanting to move onto the next part of you app, then the following is how you would transition:
- (void)loginSucceeded
{
UIViewController * vc = (UIViewController*)[self.storyboard instantiateViewControllerWithIdentifier:#"someIdentifier"];
FRLayeredNavigationController * nav = [[FRLayeredNavigationController alloc] initWithRootViewController:vc configuration:^(FRLayeredNavigationItem *item) {
item.width = 300;
item.nextItemDistance = 90;
}];
[self presentViewController:nav animated:YES completion:nil];
}
You will need to set the Storyboard ID to that specified in the above methos. This can be found in the 'Identity Inspector' tab when viewing the storyboard and selecting the ViewController you have designed.
Also you will no longer need to perform the segue you had previously created, so delete that.
Any future view controllers you want to 'push' onto the screen, you simply need to call the following:
UIViewController * vc = (UIViewController*)[self.storyboard instantiateViewControllerWithIdentifier:#"SomeStoryboardIDHere"];
[self.layeredNavigationController pushViewController:vc inFrontOf:self maximumWidth:YES animated:NO];

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

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 !

Present a Different View Controller After Dismissing a Modal View Controller

I present a modal view controller for various UI settings in an iOS app. One of those settings allows the user to select a different main view. When they hit "Done" I want to dismiss the modal view and have the newly-selected view controller appear, without a momentary delay where the old view controller segues to the new view controller. How could this be implemented?
Update:
Here is a method I successfully implemented using Eugene's technique, but without the app delegate. Instead, this implementation is specific to my scenario where a view controller in a navigation stack presents the modal view controller in a Utility app.
- (void)swapFrontSideViewController;
{
UINavigationController *navigationVC = (UINavigationController *)[self presentingViewController];
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:navigationVC.viewControllers];
UIViewControllerSubclass *selectedViewController = nil;
if ([self.selectedFrontSide isEqualToString:FRONT_SIDE_NAME1]) {
selectedViewController = [self.storyboard instantiateViewControllerWithIdentifier:FRONT_SIDE_NAME1];
} else if ([self.selectedFrontSide isEqualToString:FRONT_SIDE_NAME2]) {
selectedViewController = [self.storyboard instantiateViewControllerWithIdentifier:FRONT_SIDE_NAME2];
}
if (selectedViewController) {
[viewControllers replaceObjectAtIndex:viewControllers.count -1 withObject:selectedViewController];
[navigationVC setViewControllers:viewControllers];
self.delegate = selectedViewController;
} else {
NSLog(#"Error: Undefined Front Side Selected.");
}
}
- (IBAction)doDismiss:(id)sender {
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; // Get the app delegate
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:appDelegate.navigationController.viewControllers]; // fetch its navigationController viewControllers stack
UIViewController *replacementController; //initialize replacement controller
[viewControllers replaceObjectAtIndex:viewControllers.count -1 withObject:replacementController]; // replace the top view controller in stack with the replacement one
[appDelegate.navigationController setViewControllers:viewControllers]; //change the stack
[self dismissModalViewControllerAnimated:YES];
}

Correct pattern for refreshing data in UIViewController's

I am trying to refresh my view data when the application becomes the active app again. I believe the correct pattern is to have the app delegate tell it's view to reload it's data when applicationDidBecomeActive is called.
However, I am having trouble finding the UIViewController from within the Delegate:
- (void)applicationDidBecomeActive:(UIApplication *)application {
MyFancyViewController* controller = //how do I get my view controller???
[controller refreshData];
}
Also, can I count on the view controller still being allocated, or is there a chance it would go away? I'm using iOS 5 Storyboard's if that makes any difference.
Update:
I think I got it:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UIViewController* root = _window.rootViewController;
UINavigationController* navController = (UINavigationController*)root;
OctainViewController* mycontroller = (OctainViewController*)[[navController viewControllers] objectAtIndex:0];
[mycontroller refresh:nil];
}
Yeah, this does the trick:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UIViewController* root = _window.rootViewController;
UINavigationController* navController = (UINavigationController*)root;
OctainViewController* mycontroller = (OctainViewController*)[[navController viewControllers] objectAtIndex:0];
[mycontroller refresh:nil];
}
Why not refresh your data in the respective view controller viewWillAppear method?

Orientation problem

i have created and ipad application.
i started off with window based application and added 2 view controllers(loginviewcontroller , detailviewcontroler ) . both have their own XIB'S. the added the loginviewcontroller object in in the appdelegate applicationdidfinishlaunch method , i wrote code to move back and forth between 2 views. Here is the code.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
Login *mylogin = [[Loginviewcontroller alloc] init];
[window addSubview:mylogin.view];
//detailview *tv=[[detailviewcontroller alloc] init];
// [window addSubview:tv.view];
[self.window makeKeyAndVisible];
return YES;
}
The problem is that the willrotatetointerfaceorientation method runs only from the loginviewcontroller class even if i am in the detailviewcontroller.
if i add the tickets view to the appdelegate then it runs the willrotatetointerfaceorientation method from detailviewcontroller , so in summary it runs the willrotatetointerfaceorientation method from only the object which was added to appdelegate.
how do i make the 2 view controllers run their respective willrotatetointerfaceorientation methods?
UIViewController has the method
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
override this method in your both viewcontroller classes(i.e. (loginviewcontroller , detailviewcontroler )
write this code
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
//write your code which you want to during rotation
return Yes;
}
The rotation messages are being sent only to the controller of topmost view. You should forward them manually in appropriate methods.