What are the pros and cons of below two approaches of presenting View Controller ? What's the difference ?
UIViewController *abcVC = [self.storyboard instantiateViewControllerWithIdentifier:#"abcVC"];
[self presentViewController:abcVC];
or
UIViewController *abcVC = [self.storyboard instantiateViewControllerWithIdentifier:#"abcVC"];
[self.navigationController presentViewController:abcVC];
The first method, you mention will work only when the parent view controller is not having the navigation controller,
The second method you mention will work only when the parent view controller is the navigation controller,
but both method will present the view controller,
Correction here [self presentViewController:abcVC]; is deprecated method and new one is
[self presentViewController:abcVC animated:YES completion:nil];.
Both above mentioned will do same thing and you don't need to use self.navigationController to present a controller modally.
UINavigationController manage a stack of controllers and thats why we use it for push and pop purpose.Use UINavigationController if you need to push a controller and you want a native back button to switch back to previous controller.
i hope this will help you.
Related
There is problem in view hierarchy. Here is flow of my app.
When app starts View Controller "A" is Visible. After that storyboard "B" is loaded through "StoryBoard Reference (Push)" ,Where another navigation controller is present and Home screen is loaded. On Click of Menu button in Home screen Side panel is visible.
Now When i click on side panel menu items view Controller "B" is pushed. This View Controller is Pushed Under Home screen and is not Vsible.
Help View contoller is visible under Home controller. I want Help View Controller should come on top of Home controller.
I dont understand what issue is coming..
Any Help will be appreciated..
If you are pushing view controller from side panel shows in image, will never push because it is not in navigation controller.
the answer depends on how you showing your side menu but from assumptions
what you need to do is to set root view controller or keep reference of navigation controller and push from there
If you are presenting side menu then it is not on navigation controller so you should first dismiss this side menu and on the completion of it you should push new view controller (says B or Help). I am writing my code snippet that i am using in my project for demonstration,
- (IBAction)settingClick:(id)sender {
SettingViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:#"settingsScreen"];
[self dismissViewControllerAnimated:NO completion:^{
[self.vc.navigationController.navigationController pushViewController:svc animated:YES];
}];
}
Above method push setting view controller on current navigation controller after dismissing side menu
Now important thing is self.vc this is the object of previous viewcontroller (Home controller in your case i think) on which side menu was presented.
So my SideMenuViewController has a property like,
#property (nonatomic,strong) UIViewController *vc;
which i am setting with self from previous view controller (in your case from Home view controller) something like,
SideMenuViewController *smvc = [self.storyboard instantiateViewControllerWithIdentifier:#"sideMenu"];
smvc.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:smvc animated:NO completion:^{
smvc.vc = self;
}];
And i have used [self.vc.navigationController.navigationController pushViewController:svc animated:YES]; i.e. two navigation controller to push because i have two navigation controller in my view hierarchy to push this new view controller.
You can manage that as per your setup that how many navigation controller you have !!
Hope this will help :)
After digging a lot, I found solution for this.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Home" bundle: nil];
RootController *someViewController = [storyboard instantiateViewControllerWithIdentifier:#"RootController"];
[self.navigationController pushViewController:someViewController animated:YES];
I have set another navigation controller as root ViewController of the window and it worked for me.
Thanks.
WLINewPostViewController *newPostViewController = [[WLINewPostViewController alloc] initWithNibName:#"WLINewPostViewController" bundle:nil];
UINavigationController *newPostNavigationController = [[UINavigationController alloc] initWithRootViewController:newPostViewController];
newPostNavigationController.navigationBar.translucent = NO;
[tabBarController presentViewController:newPostNavigationController animated:YES completion:nil];
So I just simply push a new UIViewController.
Then after it posts the server callback calls a method with this code from the WLINewPostViewController.m:
[self dismissViewControllerAnimated:YES completion:^{
NSLog(#"Completed");
}];
[[self navigationController] popViewControllerAnimated:YES];
if (self == self.navigationController.visibleViewController){
NSLog(#"self = visibile");
}
if (self == self.presentingViewController.presentingViewController){
NSLog(#"self = presenting");
}
}
I tried a bunch of different things and none work.
I am relatively new to Xcode but after trying
[self dismissViewControllerAnimated:YES completion]
[self.navigationController popViewControllerAnimated:YES]
[self.navigationController.visibleViewController.presentedViewController dismissViewControllerAnimated:YES completion:nil];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
and every other possibility, I am officially stumped. The WLINewPostViewController still won't dismiss.
It Logs out "self = visible"
Let me illustrate what you are trying to do
You have a navigation controller with Controller A.
Here you are trying to present another Controller B from Controller A.
Now when you get a callback from the server, you should call dismissViewControllerAnimated from Controller B to dismiss itself.
So after dismissViewControllerAnimated:completion: method call, the Controller B will be dismissed and Controller A will be shown automatically. Now you do not need to call popViewControllerAnimated: in completion block again as there is no other Controller in navigation controller to load.
If you have different use case, let me know I can provide solution.
You are presenting a view over navigationbar instead of pushing it over navigationbar.
When push you pop. When you present you dismiss. So instead of popViewControllerAnimated you need to use dismissViewControllerAnimated:completion
dismiss behaves differently depending on the receiver. From the docs:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
In short, if the vc on top calls it on itself, it dismisses itself. Anywhere else on the stack dismisses to that point, animating only the topmost vc.
What's extra confusing (for you and many others) is that the navigation vc has a stack too, and your problem is complicated further by presenting an navigation vc atop a tab-bar vc.
So what to do? The question is unclear about which vc is the receiver in the posted code (who is self in that snippet?). The text implies that self is a vc on the stack of the presented navigation vc, like...
TabBarVC --- presents ---> NavVC
| |
| --- viewControllers stack = rootVC, vc1
|
---> viewControllers for each tab
... and it's root or vc1 that wants to dismiss. If I'm right about that, then, given the docs, the solution is clear:
[self.navigationController dismissViewControllerAnimated:YES completion:^{}];
will put us back on the tabbar vc on whatever tab was visible when we did the present.
In my app I have an ECSlidingViewController declared as initial root controller via Storyboard. In my AppDelegate's didFinishLaunchingWithOptions method, I instantiate it as above:
self.slidingController = [[UIStoryboard storyboardWithName:#"AppStoryboard" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"ECSlidingViewController"];
What I want is to be able to show a global modal view controller (eg. when a push notification arrives while the app is active) without knowing which controller is currently top in the sliding controller.
What I do is (in my AppDelegate):
[self.slidingController.topViewController presentModalViewController:controller animated:YES];
but it doesn't seem to work.
Is there any way I could present a modal controller from my sliding controller regardless which controller is topViewController?
PS. If no, is there any chance that what I want will work with SWRevealViewController instead of ECSlidingViewController? If it's worth, I will take the painful road to switch.
Thank you in advance!
If the ECSlidingViewController is set as the initial view controller in the storyboard, then why are you instantiating another one in your app delegate code? By doing that, you're calling your methods on a different instance of ECSlidingViewController than the one that's put on screen by the storyboard. This is likely the source of your problem. Instead, get a reference to your ECSlidingViewController like this:
self.slidingController = self.window.rootViewController;
Then try,
self.slidingController.topViewController presentModalViewController:controller animated:YES];
or
self.slidingController presentModalViewController:controller animated:YES];
I haven't worked with ECSlidingViewController, so I don't know which of these might work.
Try this
UIViewController *rootViewController = self.window.rootViewController;
// You now have in rootViewController the view with your "Hello world" label and go button.
// Get the navigation controller of this view controller with:
UINavigationController *navigationController = rootViewController.navigationController;
[navigationController.topViewController presentModalViewController:controller animated:YES];
I want to know what is the different of them.
when I can call
[self dismissViewControllerAnimated:YES completion:nil];
and when I should call
[self.navigationController popViewControllerAnimated:YES];
according document of apple:
dismissViewControllerAnimated means
"Dismisses the view controller that was presented by the receiver."
But I always fail to dismiss view controller by this method.
-dismissViewControllerAnimated:completion:
Used to dismiss an UIViewController, which was presented by the method:
-presentViewController:animated:completion:.
-popViewControllerAnimated:
Method of UINavigationController is used to pop a controller shown by
-pushViewController:animated method of UINavigationController.
In the first case the view controller's view shows as a modal controller (usually from bottom to top), and the second case you are pushing a view controller in the navigation stack of UINavigationController.
your selected application is navigation based application means
[self.navigationController popViewControllerAnimated:YES];
your selected application is other than the navigation based application means
[self dismissViewControllerAnimated:YES completion:nil];
I have a navigation controller named navController made programmatically in my modal view controller during its viewDidLoad:
self.navController = [[UINavigationController alloc] initWithRootViewController:self];
self.navController.view=self.view;
[self setView:self.navController.view];
But when i launch the modal view controller i dont see the navigation bar, just the standard view i made in IB. Whats wrong?
Your solution cannot work.
Suppose that you have your modal controller called ModalViewController. It's a simple UIViewController linked with a xib created interface.
Now, at some point you need to present ModalViewController modally. As you wrote in your specification, I think you want to use also a UINavigationController and control its navigation bar.
The code to do this could be the following, where presentModally could be a method that it's not contained in ModalViewController.
- (void)presentModally:(id)sender {
ModalViewController *modalController = [[ModalViewController alloc] initWithNibName:#"ModalView" bundle:nil];
// Create the navigation controller and present it.
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:modalController];
[self presentViewController:navigationController animated:YES completion: nil];
}
Now, within viewDidLoad of your ModalViewController you have access to navigationController property. In this manner you can control navigationController behaviour. For example:
- (void)viewDidLoad
{
[super viewDidLoad];
// the code changes the title for the navigation bar associated with the UINavigationController
self.title = #"Set from ModalViewController";
}
Some notes
To understand how UINavigationController works read UINavigationController class reference
To understand how modal controllers work read Modal view controllers documentation
The code I provided is a simple example and only demonstrative (I've written by hand so check for syntax). You need to make attention to memory management and how to present modal controllers. In particular, as Apple documentation suggests, to present modal controllers you need to follow these steps:
Create the view controller you want to present.
Set the modalTransitionStyle property of the view controller to the desired value.
Assign a delegate object to the view controller. Typically the delegate is the presenting view controller. The delegate is used by the presented view controllers to notify the presenting view controller when it is ready to be dismissed. It may also communicate other information back to the delegate.
Call the presentViewController:animated:completion: method of the current view controller, passing in the view controller you want to present.
Trigger (when necessary) some action to dismiss the modal controller.
Hope it helps.