iOS - pushViewController vs presentModalViewController difference - ios

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/

Related

presentModalViewController vs pushviewcontroller memory cosumption

I have two method to jump from one viewcontroller to another
For presentViewController
[self presentModalViewController:view animated:YES];
For pushViewControlle should use
[self.navigationController pushViewController:view animated:YES];
Which Will be best approach ?
Which will cause more memory leak?
Which one is use if Our Design is like
Introduction view (bunch of slides )-> login -> signUp-> HomeActivityScreen-> Then Bunch of tab bar in it
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.
You use modal view controllers to focus the user's attention on a Task. When you push, the user is in some kind of navigation flow, but still has the total application at their fingertips. They might decide to go forward or backward, switch to a different tab in the middle, whatever. When they get a modal view controller, they can't do any of that until the task is completed or canceled out of (the modal view is dismissed)
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.
Modal view controllers provide interesting ways to manage the flow of your application. Most commonly, applications use modal view controllers as a temporary interruption in order to obtain key information from the user. However, you can also use modally presented view controllers to implement alternate interfaces for your application at specific times.
So, from my understanding this one is best option.
[self.navigationController pushViewController:view animated:YES];
Apple handles the memory for both of these through Automatic Reference Counting. Although pushing a view controller might require more memory than presenting the same, the allocated memory is released when popping or dismissing the ViewController. ARC releases the said memory if no strong references are made from within the popped ViewController, or in other terms, the ReferenceCount is 0. So care must be taken so that no strong references are retained to the ViewContoller's objects.
Now whether to present or push a ViewController depends solely on your app design. Read this to get an outline on when to present a ViewController, .
They are two approaches to navigate from one view controller to other view controller.
By default:
- pushviewcontroller: you will have a back button to return to last visited page. your viewcontroller is put on top of the navigation stack. Think like a webpage when you navigate between pages.
- presentmodalviewcontroller: you have nothing to comeback to last visited page. It is usually used for: a pop-up or to change to new branch of navigation.
The use of pushviewcontroller & presentmodalviewcontroller does not produce memory leaks.
For your application flow, I suppose that HomeActivitiesScreen is one of viewcontroller in your "bunch of tab bar". If so, create a viewcontroller that contains that tabbar and make it as root of your application (named RootViewController for example). Then:
When application is launched, you show the RootViewController
And immediately, present the modal view to your "introduction pages", then login/signup. This modal view start with a Navigation view controller structure.
When the user is connected, dismiss your modal view, return back to your HomeActivitiesScreen and refresh contain if need.
Like this, you do not keep the reference to your login/signup screen when you do not need.

In WatchKit, calling presentControllerWithName from a Modal view anchors the presenting view as root

I have an WKInterfaceController containing a WKInterfaceTable which calls the
- (void)presentControllerWithNames:(NSArray *)names contexts:(NSArray *)contexts
to present 2 paged controllers modally. If I call
- (void)presentControllerWithName:(NSString *)name context:(id)context
from one of those controllers, or more specifically
- (void)presentTextInputControllerWithSuggestions:(NSArray *)suggestions
allowedInputMode:(WKTextInputMode)inputMode
completion:(void (^)(NSArray* results))completion
and then dismiss the newly presented controller, I loose the Cancel button on the presenting modal controller. So I have no way of getting back to the root controller.
Your question is a bit difficult to understand, but I believe the root cause of the issue you are having is that you are presenting a modal interface controller on top of another modal interface controller which is not what you want to do. You will run into issues like you are seeing such as Cancel buttons not appearing.
Instead, I would encourage you to think outside the box. Instead of presenting a modal on top of another modal, what if you switched page sets using the WKInterfaceController reloadRootControllersWithNames(_:contexts:). Then you would no longer have your modal on top of a modal problem.
In summary, navigation design in Watch Apps is a bit tricky, but can generally work out well if you follow the common rules.
Only use a hierarchical or page based layout system
Don't present modals on top of modals
Hopefully this helps shed some light.

Changing ViewController programmatically

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.

iOS - How to push to a view controller and maintain the correct "View Stack" when going back

I've got a single navigation controller that houses the primary view of the app (A dashboard of sorts) which then branches off to the other views in the app via segues etc..
There's a hamburger menu, which makes use of a Cocoa Pods sliding view controller. Basically, my question is is it possible to navigate to specific view controllers via the menu and maintain the correct "stack" going back. I'm aware you can just push to a single controller, but I'd like (if possible) to retain the view hierachy going back.
E.g.
There's a link in the menu to Page 1.3
I want to be able to push to Page 1.3
Then have the back button on that view to go to Page 1.2, then Page 1.1 etc..
I'm not sure whether this is standard practice in iOS apps, or whether it's better to adopt an Android esque approach and just add views to the stack, not worrying about trying to form the correct navigation structure when they go back through the views.
Hopefully all of that makes sense.
If you are happy to do it in code, rather than a segue, you can use the UINavigationController's setViewControllers:animated: method (see the documentation). You will need to instantiate the relevant VCs, initialise them with any relevant data/state, and then build an array with them in order from the root view controller to the top view controller:
NSArray *newStack = #[rootVC, ..., page11VC, page12VC, page13VC];
[self.navigationController setViewControllers:newStack animated:YES];
Then popping from page13VC will go to page12VC, etc, etc. You can get rootVC and any other pre-existing VCs using the viewControllers property of the navigation controller.
I meet the same problem in my project,here is my experience.
If you just want to do simple jump, you can try the three basic navigation function:
[self.navigationController popToRootViewControllerAnimated:YES]
[self.navigationController popToViewController:[[self.navigationController.viewControllers count] - mypopcount] animated:YES];
[self.navigationController popViewControllerAnimated:YES];
Bind the action with the button, you can do that.
But, if you want to do more flexible page jump in ios, you must consider:
First of all, you need to confirm that there is no direct data interact between these view controllers, which means you need coreData or other sqlite tools to store data, so the destination page can load data itself.
Secondly, you should consider the strategy, page jump means pop all viewcontroller and push a new one, or just pop or push a new one just on the navigation stack.
Good Luck!

Modifying UINavigationController stack with modal view controllers

I have the following environment:
root view controller is UINavigationController
a number of custom UIViewControllers may be pushed on the navigation stack
each of custom controllers may or may not present a modal view controller
I need to be able to programmatically manage the navigation stack (for example - drop all controllers from the navigation stack except the root controller as response to some external event like push notification delivery)
Naive implementation with [navigationController setViewControllers:newControllers animated:animated]; obviously fails if there was a modal view controller presented by any of old controllers ind the stack:
This modal controller stays visible
If a delegation pattern is used for parent<->modal controllers communication (parent is delegate of presented modal view controller) any action in modal view controller results in crash since delegate was already released
So the general problem is that modal controler lifcycle is not bound to the parent controller. My questions are:
Is there a stadard approach for managing this kind of hierarchy and safe navigation stack changes?
If NO than what would be a best custom implementation? I'm seeing two general approaches - one is to explicitly dismiss/unlink all modal contrellers in the code changing the navigation stack, the other is to add logic to parent view contollers to manage modal controllers lifecycle directly.
There is no standard approach because this behaviour is discouraged by the HIG. Even in the event of push notifications, you're not supposed to modify the existing stack except through pushes, pops, and pop-to-root. However, what you're asking is completely possible.
You have several options, but the best is probably notifications. Use NSNotificationCenter in your app delegate to let any interested view controller's know that you're about to pop to your root view controller. In each of your modal view controllers, register for this notification name and dismiss yourself when notified. After dismissing your modals, you can just use popToRootViewController and avoid messiness with modifying the stack.
Take a look at TweetBot and see how they handle push notifications. They do a pretty good job, I believe, and they just present a new modal view controller. You can do this (nested modal presentations), so experiment around and see what you can do without jarring the user.
Check this method:
http://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html#//apple_ref/occ/instm/UINavigationController/popToRootViewControllerAnimated:
Also, modal controllers are modal for a good reason - to draw user’s attention on one task or unit of work or whatever your app does. So, if you want to programatically hide it and return to some other controller in navigation stack, it’s kind of obvious you need to deal with the modal controller first.
call [self dismissModalViewControllerAnimated:YES]; from -(void)viewDidUnload method of the viewcontroller.

Resources