Thoughts about UINavigationController - objective-c

For my new app I'm planning to use UINavigationController to push/pop other controllers.
Here is the scenario.
Application is running. Via navigation controller I push first controller on the stack. The user make some selections and touch a button. Then navigation controller push the second controller and so on while the user reach the last controller which is sixth. Controllers from first until fifth will never be used again in the app.
Is this the correct approach (using navigation controller) for such kind of app ?

I am not entirely sure what you mean, but I guess you need to walk the user through step 1 to 5, then when they done at 6, they cannot go back. Is that correct?
I did something similar. What I did was pop up view 1-6 modally(and navigate from 1 to 6) to interrupt from the current flow, and once the user's done, the value got passed back to the view where you populate the modal view from(delegation) and then you do whatever next.
Not sure if i answered your question. Hope it helps.

If once they reach the 6th viewController they will never go back to the other viewControllers you can always pop to the rootViewController and then push the 6th viewController on. That way those other viewControllers are not in the navigationController stack.

It sounds like you have a flow of 5 linked screens, and then the rest of your app.
If so, yes, UINavigationController would work fine here. You would push those 5 screens, and when its done you would destroy the navigation controller and replace it with some view for the rest of your app.
So the UINavigationController would control one part of your app, but not your entire app.

Related

UINavBarController connecting the same UIViewController to multiple navigation controllers

I have a storyboarded app with a chain of tableviews followed by a detail view. Kind of the classic iPhone app. There are 4 tabs and each one leads to a navigation controller.
The issue is I really want to avoid unnecessary glue code since the app is basically finished. If it was possible to connect the Search and Favorites (bottom two off the tab bar) controller as 'Root View Controllers' to the same UIViewController I would be done. However, this won't work since a view controller can only be the root view controller to one tab. So as you can see I've instituted two dummy UIViewControllers that forward you to the UIViewController in the middle. Now, unfortunately, I have to write code to make that central view controller a fake root view controller to disable the appearance of the back button, and prevent popping to the blank root when you double-tap the tab bar.
Has anyone got a more elegant solution?
This appears to be a flaw in Storyboards. One workaround would be to use simple view controllers for each navigation controller's rootViewController. Put a UIContainerView in each that points to the UIViewController you want to share.

Make a view to be the primary one in navigation controller?

I am using a tabbar, each tab having a navigation controller, and this in turn having a stack of views. Each view is having its own view controller but this is not important now.
Lets have a tab 1 with a navigation controller 1 with views A, B, C.
The nature of the application dictates however that the view B is the primary one.
So what I want is that by default (after first or after relaunch of the app), when I tap the tab 1, I will see the B view together with the back button to A view.
How can I achieve this?
You can set up a delegate for your tab bar controller and implement tabBarController:didSelectViewController: to detect when someone taps a tab. If you detect that A is about to be selected and you want B to be displayed instead, you can tell A's controller to use its navigation controller to push B's controller.
Try initializing and pushing view B (without animation) onto the navigation stack in viewDidLoad of view A.
You could use the setViewControllers:animated: of UINavigationController.
Depending on your exact needs you could set this in your app delegate applicationWillEnterForeground: or applicationDidBecomeActive: methods.
I recall there being an Apple sample app that does exactly this. The general idea is to save the last visible view controller (or just hard code the one you want) and then push it to the visible state using something like so:
[myNavigationController pushViewController:viewControllerToBeVisible animate:NO];
You'd want to show the apps tabBarController:didSelectViewController:, and handle the different cases based on which viewController was selected.

addSubview: re-writes the navigation stack?

I'm working on a app which uses the controller containment pattern as described in the View Controller documentation in the iOS SDK.
I've written the controller container, and it works great. My controller is basically containing two sub views and it displays them both at the same time, sliding one over the other depending on what the user is doing. Works wonderful.
Now, I want to use this container controller in a navigation view. This is to get push segues to work. In effect, from my contained controllers, I want to be able to use the navigation stack, push a new controller on, and pop when the user is done.
However, I have noticed that if the navigation view is instantiated with my container controller as the root container, things fall apart.
In particular, I have noticed this:
In the iOS documentation, container controllers call addChildController: and then addSubview:. This seems to break the navigation stack, as the push segue does not work - it behaves like modal. I believe it does this because addSubview resets the navigation stack.
I confirmed this by replacing addChildController and addSubview with [self.navigationController pushViewController...]. I confirmed it is a problem with addSubview because I can reproduce the issue when I omit the call to addChildController.
When I do this, the navigation stack works properly. But of course, my container controller does not, as only the "most recently pushed" controller is visible.
I'm doing this because in my contained controllers, I want to push a new controller onto the stack, and when the user is done, I want to "pop" the stack, without reloading the "previous controller".
Using a modal segue reloads the previous controller; using a push controller does not.
I cannot find any documentation on the behavior of addSubview and it's effect on the navigation stack.
Thank you in advance for any light you guys can shed!
I'm having a bit of trouble completely understanding what you are doing, but I think that what you want to do is exactly what I'm doing.
I have a UINavigationController that has as its rootView a container UIViewController. That controller adds children per the normal methods. One of those children views pushes other views that may get popped.
One of those pushed views COULD message the appDelegate and make itself the rootViewController if it wanted to. In general, as long as you keep a strong reference to a view controller, you can remove it from whoever 'owns' it, and muck around with the navigationControllers viewControllers array to your hearts content.

Modal UINavigationController hides although not dismisses

Okay, so I'm building an universal iOS app with an initial login view (view controller named LoginVC), just a plain simple UIViewController. If the login is successful the app segues to an navigation controller (MainNavigationVC). I created this segue through the storyboard gui of XCode, so no programmatic creation of the nav controller is done. The nav controller is presented modally in fullscreen, so the rest of the app is run atop the login view, with this nav controller as the centerpiece of everything.
The navigation controller contains a view (with a view controller named UserStartPageVC), and in its navigation bar is a logout button. This button sends an target action to UserStartPageVC, with the goal of dismissing the nav controller thus bringing the user back to the login view.
So far everything works fine. I can login and use the app as intended. But! When I log out and then re-login XCode tells me this:
Warning! Attempt to present <MainNavigationVC: 0x753110> on
<LoginVC: 0x756fcf0> while a presentation is in progress!
I suppose this means that the login view is trying to modally display a MainNavigationVC navigation controller, but another one is already displayed, right? But how? Can a view be presented without showing?
And how can I get rid of the old nav controller when logging out? I've tried several ways of dismissing the modal view, for instance:
from within UserStartpageVC running
[x dismissViewControllerAnimated:YES completion:NULL]
[x dismissModalViewControllerAnimated:YES]
where x is either self, self.parentViewController or self.presentingViewController.
setting the LoginVC as a property in UserStartpageVC and running
[self.loginVC dismissViewControllerAnimated:YES completion:NULL]
and so on.
All of the tested calls actually brings me back to the login screen, so it's kind of working.
Any ideas? Relevant code samples can be provided if necessary, I just couldn't figure out which pieces that were of interest. The seguing to the navigation controller has no code (except for a performSegueWithIdentifier:sender:), and the code for dismissing it is the part I cannot seem to get straight.
As a sidenote. So far this isn't a REAL problem; the app runs, and it IS possible to logout and re-login without any other side-effects than an error message in XCode. But I suppose this will be a memory leak if users logout and login multiple times, and I'm not in the mood of an unnecessary rejection from Apple.
I discovered another way to get the exact same error message. Lucky me!
If you created a segue at one point and had it tied to a button (click button -> new view) and then later give that segue a name and invoke it directly using
[self performSegueWithIdentifier:#"identifierName" sender:self];
then you can get this error because you can effectively trigger the segue twice. I thought making the button invoke an IBAction would turn off the segue I had set up in the first place, but apparently not. Hitting the button triggered the segue twice, but after I deleted the segue and re-created it as a manual segue on the view with the same identifier then I was able to invoke it via the above code and not get the warning message.
Hoopla! My bad.
Seemed I had set up the notification observing from the login API call in a stupid way. For every time the user triggered a login (or re-login), it added itself as an observer for the notification; the result was that it performed one more segue for every time a login was done.
And doing multiple segues at the same time, is... well, obviously bad.

MonoTouch: StoryBoarding - manual segues?

iPhone/iPad dev newb here...
I am using MonoTouch to create a universal iPad/iPhone storyboard app. In the primary view controller (RootViewController) the default auto-generated behavior is a table with a single cell "Detail" in it, which hardwires you to the next destination (DetailViewController).
I'd like to change this RootViewController to instead show a login control I've made extending UIViewController (LoginView). I have had some success putting the LoginView inside the RootViewController, but can't figure out how to make it 'segue' to DetailViewController. And upon watching how the iPad app works, where there is no segue (that I can see) between the two, am I going about this wrong?
To summarize: How do I enhance this storyboard app with a preceding login screen, reusing its contents/xib between the iPad and iPhone variant?
Apologies if this is a bit unclear and muddled.......
I have not tried a storyboard app in Monotouch yet, but I did spend a lot of time working out the best approach for a login screen for my application. What I ended up doing, which could work in your situation as well, is presenting the login screen as a Modal view. Once the user has successfully logged in, we then push the next appropriate view.
So if you are automatically pushed into the detail view by the storyboard viewcontroller, you could display the login dialog as a modal dialog from the detail view's DidLoad or DidAppear methods.