I am doing something wrong, but not sure what it is. Bellow is the diagram of my storyboard.
/----(push)----login \
Start nav ctrl mainview nav ctrl
\----(modal) register /
When the user goes through login an event is received by appdelegate logindone, in login done I do:
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"MainView"];
This works just fine.
However if the user goes through register and completes registration with login the same loginDone is triggered but the code above results in blank view.
If I replace above code with
UIStoryboard* storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
MainCtrl *mainViewController = (MainCtrl*)[storyBoard instantiateViewControllerWithIdentifier: #"MainView"];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
self.window.rootViewController =navigationController;
This almost works, the view is displayed correctly, the only problem is that navigation bar is blank and it stays blank no matter where I go. Restarting app works of course because it bypasses register controller.
in register I do [self dismissViewControllerAnimated:NO completion:nil]; before calling loginDone, but this really makes no difference.
Any suggestions?
Edit:
self.window.rootViewController = [self.window.rootViewController.storyboard
instantiateViewControllerWithIdentifier:#"MainView"];
is the only thing that will work in normal flow i.e after login. Otherwise I get a crash "unavailable segue".
The way I fixed this is not exactly the way I wanted to fix it, but it works. I had to modify login done function that used to handle login finish work and transition to main view passing it parameter not to transition. On return I trigger a segue to main view from registration view. Still not sure why it was not working the way I had it done originally.
Related
I'm trying to see a different viewcontroller whenever my app gets a push notification. I tried to use
[self performSegueWithIdentifier:#"shootPicture" sender:self];
and
[self performSelectorOnMainThread:#selector(performSegueWithIdentifier:sender:) withObject:#"shootPicture" waitUntilDone:NO];
and
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *BombaViewController = [storyboard instantiateViewControllerWithIdentifier:#"BombaViewController"];
[self presentViewController:BombaViewController animated:NO completion:NULL];
All af the above example works, but they work JUST ONCE! After the viewcontroller goes back to the main tabbed controller, it doesn't work anymore.
I've also used NSLog to make sure that the system can actually intercept the push notifications.
Any suggestions?
I just put a button and dragged it to the starting Tabcontroller
This would indicate you have a new push / modal segue.
So, you aren't really going back, you're going to a new copy of your tab controller which is sitting in front of everything else. What you should really be doing is unwinding the segue that got you there, or connecting the button to an action in your code and calling dismissViewControllerAnimated:completion:.
In our storyboard we have multiple UINavigationController stacks. For example, the LoginViewController stack is completely separate from the SWRevealViewController stack.
What's the best practice for switching between them? When I press the logout button (the logout button is on the SWRevealController stack) and then try to present the LoginViewController stack, I get an error like this:
Warning: Attempt to present LoginViewController on SWRevealViewController whose view is not in the window hierarchy!
Even though I'm specifically setting self.window.rootViewController to the Login View Controller's UINavigationController in App Delegate like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Spitfire" bundle:nil];
UINavigationController *nav = [storyboard instantiateViewControllerWithIdentifier:#"LoginNavigationController"];
LoginViewController *loginVC = [storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
self.window.rootViewController = nav;
[nav presentViewController:loginVC animated:NO completion:nil];
Is there a way I can "dismiss" the current UINavigationController stack and use a new one? Or maybe I shouldn't be calling this code in my app delegate?
I had not played with SWRevealViewController before your question, so my previous response was a little more generic. I've created a sample app here that does its best to throw around as many navigation stacks as possible; I'm sure I've covered your use case.
A few things I'll draw your attention to:
The "main flow" from my previous answer is right smack in the middle of the storyboard; that should help orient you.
SWRevealViewController.m can be used in conjunction with Storyboards and has a very clever hack to do so, but the documentation for it is buried in the implementation. It took me a while to figure out what the hell was happening, but my example app illustrates the usage (two segues, one for "front" and the other for "back", could have a third for "right").
The app's main flow gets completely swapped out for the alternate flow. This is by design of SWRevealViewController. You are essentially switching to an entirely new app.
All sub-flows that are presented modally disrupt the flow.
The author of SWRevealViewController has not provided UIStoryboardSegue subclasses for the replacement of front, rear, and right view controllers. This would be trivial to implement, but I've elected to do the swapping manually in FHKSettingsViewController's implementation.
I repeated code and made dinky classes to make sure what I was doing was obvious.
You should wait to call presentViewController until viewDidAppear on rootViewController.
See here for more discussion: whose view is not in the window hierarchy
Edit:
call presentViewController from your UINavigationcontroller's rootviewController in viewDidAppear: method
-(void)viewDidAppear:(BOOL)animated {
LoginViewController *loginVC = [storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[self presentViewController:loginVC animated:NO completion:nil];
[super viewDidAppear:YES];
}
I am returning to my login view using the code below. The view loads correctly and everything looks fine. All buttons work etc.
JALoginViewController *loginVC = [[JALoginViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:loginVC];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self.navigationController presentModalViewController:navigationController animated:YES];
However, if a user tries to log in again, the segue that takes them to the next scene can't be found.
I'm using performSegueWithIdentifier if the users login credentials are correct, like this:
[self performSegueWithIdentifier:#"loginSegue" sender:self];
This is the error I receive:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<JALoginViewController: 0x8d614b0>) has no segue with identifier 'loginSegue''
I've done lots of searching on Google and through the docs for the solution to this, the closet I've got (at least I think) is this question. The explain and solution sound like they could be correct and relevant, but I can't put them into practice.
Documents I've read and tried:
initWithRootViewController
popToRootViewController - The current root view controller is for a tab bar - not the login scene I need so as far as I'm aware I can't use this.
popViewControl
pushViewControl - This works to an effect, I don't think it is the correct way though. I don't want there to be navigation bar and I don't want my tab bar to appear when the user returns to the login scene.
I've tried various methods with limited / no effect. At this stage any help would be greatly appreciated.
Please let me know if I haven't provided enough information.
Thanks
JA
Edit - Zoomed out image of storyboard
![Zoomed out image of Storyboard][1]
On the basis of the screen snapshot of the revised question, from your rightmost red-highlighted scene, you should be able to:
[self.tabBarController dismissViewControllerAnimated:YES completion:nil];
and you'll be back at that initial screen (I'm assuming you did modal segue from initial screen to your tab bar controller).
Original answer:
If you want to manually push to a view controller, rather than creating it via alloc/init, you should use
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"loginsSceneStoryboardIdHere"];
or, if that view controller was the "initial" scene (the one with the simple arrow coming in from the left), you could use
UIViewController *controller = [self.storyboard instantiateInitialViewController];
And you shouldn't be manually creating the navigation controller, either. If the loginVC needs a navigation controller, you should embed that scene in a navigation controller right in the Interface Builder, then give that new navigation controller its own unique storyboard identifier, and then you can
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"navControllerSceneStoryboardIdHere"];
[self presentViewController:controller animated:YES completion:nil];
I must confess that I'm worried by this whole "return to login via pushViewController" construct. I assume you know that you're not "returning" to it, but creating a new copy of it. If you push/modal from A to B and then B to C and then C to A, you're holding 4 views and their controllers in memory, two copies of A and one of B and one of C (which is, obviously, not good). I just wanted to make sure you don't have a circular set of segues and/or push/presentViewController references.
If the login is the initial scene in your app and if you've been doing only push segues (no modal segues along the way), you can do a:
[self.navigationController popToRootViewControllerAnimated:YES];
That will take you to the top level view controller, and it will pop off and release all of the intervening scenes.
If you're using iOS 6, you can avail yourself of the unwind segue, which can achieve the same functionality, but it doesn't care whether the preceding segues were pushes or modals.
There are lots of ways of skinning the cat, but generally doing a new presentViewController to the first scene in your storyboard is a very bad idea.
I'm relatively new to iOS development. I am to move from one viewController to another I use a modal segue transition on button click. This is a game so i want to allow the user to click images to essential move the the app menus.
I have a main page that displays several images, on clicking one i want to be able to move to another view. Currently doing this with a modal segue is causing odd problems with my touchesEnded event where if, for example, i navigate to a page 3 times the touchesEnded event is fired 3 times.
Is there a better way for me to do this or am i just missing thing fundamental?
Thanks
Yes, I think you must make the the navigation controller your root view controller then push views accordingly
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:YOUR_BASE_CONTROLLER]
self.rootViewController = nav;
this is in your app delegate.
Then in your action method
[self.navigationController pushViewController:secondViewController animated:YES]
Im assuming you are using the Storyboard to link VCs using segues.
Modal segues are great for simple transitions but really seem to limit what you can accomplish when they are just linked through SB. Ive found that creating an IBAction that includes the following for a VC segue will allow you to not only control your segues more efficiently but also allow you to have a clearer view of what is actually occurring during the transition.
-(IBAction)goToVc:(id)sender{
//Other code to take place during the segue here
//This will identify the Storyboard in use
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
//This will identify the View Controller to switch to
SecondViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewControllerID" ];
[self presentViewController:vc2 animated:YES completion:NULL];
}
Every time you perform a modal segue, you lose the UINavigationController that you were previously using. What you need to do is embed a UINavigationController in the view that you are performing a modal segue to.
Check out a question I answered the other day to help you visualize more clearly what I'm talking about.
I have an app that starts out in the login screen and when you log in it pushes a modal TabBarController. One of the tabs is settings which has a logout button, what would be the correct way to log out of my app and not have any issues such as memory leaks?
It really depends on how your users are logging in. What you probably need to do is the opposite of whatever you are doing to login. If all the login does is open the modal dialog, then closing it should be fine. It you are setting some kind of security token, then you will need to set it to nil.
Without knowing more about how your app works, there isn't much more I can say.
I know this is old, but if you want to present your login screen (because you logged out) as a modal, you can do this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
//Your login view controller, make sure you set the storyboard id
TTTLoginController *log = (TTTLoginController *)[storyboard instantiateViewControllerWithIdentifier:#"log"];
//wrap it in a navigation controller
UINavigationController *navBar=[[UINavigationController alloc]initWithRootViewController:log];
//present the modal view
[self.navigationController presentViewController:navBar animated:YES completion:nil];