I have a fairly big app made with all view-controllers connected by storyboard segues, before I realized that navigation buttons is a requirement.
I am at a point where I can no longer delete all scenes and start over, nor I can extend the segue approach because its quite overused.
I am looking up for small, elegant solution that will allow all my view controllers to have backward navigation. See my scene hierarchy:
Page 1---> Page 2
Page 1---> Page 3 ---> Page 4
Page 3---> Page 5 and so on.
So I should be able to go back from Page 5 to Page 1 backwards.
I have about 12 screens as of now.
What should I do?
Can I safely mess up with storyboard?
Or should I add code snippets to my viewDidLoads and so on to handle the stuff?
My problem is that when I searched the solution, I found too many approaches including navigation controllers, uinavigationbar, UIBarButtonItem and so on. With each of them, I find it maybe too much for my need, and I need a smaller thing that may just work.
All I may need is only back buttons as of now, but with minimal effort - as I am quite tight on deadline :-(
Appreciate quick help.
I found the answer from apple docs - basically I needed an approach to handle things (instead of straight code), and here it is:
Initialize navcontroller in appdelegate didFinishLaunchingWithOptions method, with root view controller set to first view controller of the app
set appdelegate window's rootviewcontroller = navcontroller
with every 'forward' navigation - use pushViewController along with target view controller
there is no need to handle backward navigation - navcontroller takes care of it by default.
Above will also add extra navigation bar on top of every view controller (except the root view controller of the app's delegate).
Related
I have been looking for a solution to this for quite some time but I still fail to see what the best solution is:
I am trying to swap view controllers programmatically, without anything in the storyboard, pure swift files.
As far as I see presentViewController() just creates a 'modal' which would cause the previous ViewController to stay in the memory (tested this, deinit never fires for the first controller). The solution I have found is to switch the rootViewController: self.view.window?.rootViewController = ViewController2() -> this fires deinit of the first one.
While this solution would work in theory, I am wondering...
Is there some recommended way or best practice of how to do this programmatically? Or is it really just about changing the viewRootController's value?
How do you structure your app? Do you use one ViewController and you swap views? Or you present other ViewControllers as modals with presentViewController? (I am totally new to this and I can't seem to find any good source; most of the articles deal with storyboard)
Thanks a lot!
EDIT: I will add: my test app is not supposed to have any kind of navigation for different viewcontrollers (no tabs, nor anything like that). It basically works like screen1->screen2->screen3->screen4. If a reset button is pressed, it gets back to screen1. I am purely interested of swapping ViewControllers, nothing else.
1: Changing the main window's root view controller is correct for swapping view controllers.
2: Generally, swapping view controller is not used. You can use a navigation controller that has a root view controller (eg. your main screen) and then push other view controllers as needed. Tab bar view controllers are also very useful.
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/CombiningViewControllers.html#//apple_ref/doc/uid/TP40011313-CH6-SW1
Hope this helps!
1.
The basic ways are:
navigationController?.popViewControllerAnimated(true)
or just pop to root view
navigationController?.popToRootViewControllerAnimated(true)
pushing can be done similarly
or setting a new stack
navigationController?.setViewControllers([viewController], animated: true)
These are the recommended ways of navigating in code just pick the one appropriate to your specific situation.
The push and pop mechanism is probably the most recommended way of doing general navigation.
Try to always use navigation controllers and only use modal views when appropriate.
Use navigation controllers when navigating through your main views. Modal views can be used as side views or extension to the view presenting that view modally.
Edit:
There are many ways of efficiency navigating through an app. In this particular case pushing and popping the navigation stack is probably the best way to go. But keep in mind that there are Tab bar controllers, collection views and others. At the end of the day The best is to look at your app structure and based on that decide which navigation method is the best for you.
I have read here a lots of questions and answers regarding segues and story board , and i became even more confused about the way i build the hierarchy between views
My questions are very basic and simple, and i think they may be helpful to others :
1.In a simple words, when should i use push , modal, or costume segues ?
2.When go from view1 to view2 to view3,how should i go back from 3 to 1? can i just drag a segue from 3 back to 1? and what happens with 2 and 3 with memory aspects in that case ?
3 when dismissing a view, why it always go back to the previous view? is there a way to make a segue that dismiss automatically the previous view ?
I think a very simple and short summing on the way it works would be so helpful.
thanks.
If you are using Storyboards and segues you may want to take advantage of exit segues for going back. In your example you want to go from View 3 back to View 1. Depending on how you got there, you may have multiple options to go back. For example, if you used a UINavigationController and pushed your way there, if View 1 is the rootViewController you could simply call popToRootViewControllerAnimated: and it would handle what is necessary to get from wherever you're at in the navigation stack back to the root. If View 1 is not the root you could opt to use the popToViewController:animated: API instead, assuming you have a reference to View 1 from View 3 which usually isn't ideal if you're keeping concerns separated. As Renish pointed out you do have the option of accessing the viewControllers property of your navigationController.
Back to Storyboards, for better or worse, they can handle this all for you via an exit segue. The storyboard configuration determines the proper way to go back to a view that you came from. Check out this great explanation in this answer, https://stackoverflow.com/a/15839298/250190
I think u want How to Go 3view to 1view.Simply make array of Your view controller.
How do I get the RootViewController from a pushed controller?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/Reference/Reference.html#//apple_ref/occ/instp/UINavigationController/viewControllers
Use the viewControllers property of the UINavigationController. Example code:
// Inside another ViewController
NSArray *viewControllers = self.navigationController.viewControllers;
UIViewController *rootViewController = [viewControllers objectAtIndex:viewControllers.count - 2];
This is the standard way of getting the "back" view controller. The reason objectAtIndex:0 works is because the view controller you're trying to access is also the root one, if you were deeper in the navigation, the back view would not be the same as the root view.
I am just looking for a sanity check here.
I have a screen that the user passes through on the way into the main application. That screen can be navigated back to from almost anywhere in the system.
As it stands I am just presenting ViewControllers without using a NavController to manage them (it does not seem applicable for most of my app, since screens are not necessarily sequential or related to one another).
My question is, if I have presented VC1, then navigate to other screens, and finally want to present VC1 again, I am doing something like:
[self presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"VC1"] animated:YES completion:nil];
Is this bad form? Am I leaking memory by creating a bunch of VC1 instances or is there some magic that uses the previously created one?
If it is bad form, how do I get back to the original VC1 to reuse it?
Thanks for any input.
I think you pegged it: It's not a great idea to have multiple instances of the same view controller in memory at the same time. Every time you instantiate a new view controller and present it modally, you'll consume more memory.
The most elegant solution is the iOS 6 unwind segue. But most of us would be unwilling to give up on iOS 5 support quite yet.
If you need to support iOS 5, you could contemplate using navigation controller, but hide the navigation bar if you don't like it in your user interface. Then replace modal segues with push segues and now you can do popToRootViewController whenever you want to return to the main view controller.
What is the difference beetween calling presentModalViewController and pushViewController, when :
animation is set to NO (even if yes, that's just an animation style that can be changed).
a navigation controller is defined when presenting the modal view, so it can be navigable too, with a call stack, ....
Is this just to be able to go back from the first pushed view ? Woooaaaaaa.....
I guess the difference is elsewhere and deeper. No ?
Ignoring transitions/animations and how things are structured behind the scenes (which aleph_null's alswer provides a good discussion of), the only user-facing difference is the ability to return to the preceding view automatically using the navigation bar.
If you use pushViewController you will automatically get a "Back" button in the navigation bar. If you use presentModalViewController you do not, and generally will have to implement your own controls and/or callbacks to handle dismissing the controller.
Conceptually the modal presentation style is generally used for atomic tasks that you cannot navigate away from (i.e. you either complete the task, or you cancel, and you cannot do anything else within the app until you do one or the other).
If you're wondering why have the difference in the first place, I can't say. Personally I think frameworks that provide a unified API for moving from one controller to another (like cocos2d, or Android) make a lot more sense.
The most important difference is about semantics. Modal view controllers typically indicate that the user has to provide some information or do something. This link explains it more in depth: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html
Here's another, less abstract difference they talk about:
"When you present a modal view controller, the system creates a parent-child relationship between the view controller that did the presenting and the view controller that was presented. Specifically, the view controller that did the presenting updates its modalViewController property to point to its presented (child) view controller. Similarly, the presented view controller updates its parentViewController property to point back to the view controller that presented it."
Also see this thread: why "present modal view controller"?
Take a look into the viewControllers in the image
The top 2 viewControllers(login & submit) at the top left are disconnected from the tabBarController & NavigationController
The rest of the viewControllers are embedded in a NavigationController. They somehow belong to the natural flow of the app.
Now you have to ask yourself
Do I need to always show login + submit page every time? It would be pain in the neck for the user to each time go to login even if they logged in last time. These 2 screen really don't fit the natural flow of the screens. So what do we do? We just add them modally using presentViewController
However for the rest of the viewControllers we want to keep them inside 2 navigation so we can easily go back and forth so we use pushViewController
For more information I recommend you to see this video
The image was also picked from this great answer. It's worthy of a look.
This is what my experience says,if you want to manage a hierarchy of views,better go for pushViewController in the navigation controller. It works like a stack of view-controllers in the navigation controller. If however the requirement is just to show a view on executing some actions on the parent view controller then the best way is presenting it modally.
If you need a complex push pop logic always prefer a pushViewController.
UINavigationController are used when you want to have some sort of hierarchal representation of your data (ie drill down). They work using a stack of UIViewController subclasses. Every time you “drill down”, you simply add another view controller to the stack. Then, the “back” logic is simply a matter of popping view controllers off of a stack.
You can check out this link:
http://www.icodeblog.com/2011/10/11/back-to-basics-an-introduction-to-view-controllers/
I have a CoreData app with the MainView the UITableViewController which houses all of the items in the list. What i'm trying to accomplish is adding a custom back button and using the popToViewController:animated: to access the settings. When I try to use this method the app crashes. After doing extensive reading I realized that push and pop use an NSArray stack for view controllers. For example, the rootView is view 0, when you use pushToViewController:animated: it added in another view, 1 and so forth. That all made sense. What I learned was you can't pop to a view which is not loaded into the stack after the root view. My objective here is to pop to the settings view. When I change the code around in the AppDelegate.m to make the SettingsViewController the rootViewController, the UITableViewController no longer functions, it fails telling me the entity "enityName" can't be initialized. Is there any way to still have the CoreData part of the app function correctly and still pop to the settings? I have thought of using a modal view but it ruins the style of the app.
This was quite hard for me to explain, if you didn't understand any part of it, let me know.
Thanks for your help.
Update: I read in the UINavigationBar documentation that you can use - (void)setItems:(NSArray *)items animated:(BOOL)animated thus allowing you to manually set the array of pushing and popping view controllers. I just can't figure out how to do that. I've gone through apples drillDown sample code, but it didn't have the functionality I was looking for.
Perhaps you are misunderstanding Apple's navigation controller idiom. It is meant for drilling down a hierarchical structure of views and move back and forward easily and intuitively.
A view that is outside this hierarchy (it seems your Settings View belongs to this category) should really be presented modally. On the iPad, you can even use the pretty and convenient UIPopOverControllers.
Of course, if you want to keep your own look and feel (incurring the danger of confusing your users), you could fiddle with the transition animation. You could use Apple's own and thus pre-approved UIModalTransitionStyle property of UIViewControllers.
Or you could try what you did up to now and fiddle with the view hierarchy. Maybe you can eliminate your errors simply by using the view controllers sequentially and not jumping around skipping controllers in between. In this case it should be enough to use
[self.navigationController pushViewController:controller animated:YES];
and
[self.navigationController popViewControllerAnimated:YES];
rather than the more error prone versions pushToViewController and popToViewController.