I have tab bar based application (iOS 7.1 SDK). When user start app at first time, I want show some login screen. I decided to use view controller containment (this is called in first view controller of tab bar controller):
LoginViewController *vc = [LoginViewController new];
[self.tabBarController addChildViewController:vc];
[vc didMoveToParentViewController:self.tabBarController];
[self.tabBarController.view addSubview:vc.view];
But there are some problems. View is normally visible, but in LoginViewController viewWillAppear and viewDidAppear are never called. I try to use this piece of code in all view lifecycle methods (viewDidLoad, viewWillAppear, viewDidAppear), but with no luck. I know there are some other ways to achieve what i'm trying to do. For example add child controller to first view controller of tab bar controller and hide tab bar, which works great and viewWillAppear and viewDidAppear are normally called. But because of this I get even more curious - why adding child view controller to tab bar controller don't work as expected?
You need to present or push the viewcontroller in order for the methods to get called. Just adding the view as a subview will not work.
In your case, you can explicitly call the viewWillAppear, viewDidAppear methods.
LoginViewController *vc = [LoginViewController new];
[self.tabBarController addChildViewController:vc];
[vc didMoveToParentViewController:self.tabBarController];
[self.tabBarController.view addSubview:vc.view];
[vc viewWillAppear];
[vc viewDidAppear];
It's working!
dispatch_async(dispatch_get_main_queue(), ^{
[self.tabBarController setSelectedIndex:0];
[self.tabBarController setSelectedIndex:1];
[self.tabBarController setSelectedIndex:0];
});
Related
I have a Tabbarcontroller filled with 5 Viewcontrollers and Navigationcontrollers as I did here:
[self addChildViewController:VC1];
[self addChildViewController:NavigationController;
[self addChildViewController:VC2];
[self addChildViewController:VC3];
[self addChildViewController:VC4];
Now the thing is, that pressing a button on my Tabbar gets me to every ViewController easily, where I can present Xib-Files etc.
But now I want to have a Navigationcontroller, which is shown when pressing a button on my Tabbar. This Navigationcontroller itself has several Viewcontrollers.
I tried this to present my first Viewcontroller inside my Navigationcontroller (this code is from the Navigationcontroller.m):
- (void)viewDidLoad {
[super viewDidLoad];
[self addChildViewController:VC5];
[self presentViewController:VC5];
}
This expectedly did not work and gave me: Application tried to present modally an active controller.
Is there a good way to achieve such a specific goal? I'm struggling with this problem. Thanks in advance!
edit: This is how I set it up in my storyboard. In my programmatic approach the first view controller is not shown.
Instead of adding the VC5 view controller to the NavigationController as a child (unless it's meant to be a child?) add it as the root view controller when you add the NavigationController to the tab bar.
For example in your tab bar code:
[self addChildViewController:VC1];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:VC5];
[self addChildViewController:navigationController];
[self addChildViewController:VC2];
[self addChildViewController:VC3];
[self addChildViewController:VC4];
Apple docs on UINavigationController are here: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/#//apple_ref/occ/instm/UINavigationController/initWithRootViewController:
I'm updating an iOS app and receiving the following warning when presenting a view controller: Presenting view controllers on detached view controllers is discouraged <UINavigationController: 0x984e640>.
The app delegate loads the main view controller like this:
[self.window setRootViewController:[MainViewController sharedInstance]];
[[MainViewController navigationController]setViewControllers:#[[MenuViewController new]]];
MainViewController creates a new navigation controller and in viewDidLoad the new navigation controller is added to the view with this:
[self.view addSubview:_navigationController.view];
Now I need to present another view controller. I have tried using this:
[_navigationController presentViewController:anotherViewController animated:YES completion:nil];
and this:
[[MainViewController navigationController] presentViewController:anotherViewController animated:YES completion:nil];
+ (UINavigationController*)navigationController {
return [[MainViewController sharedInstance]navigationController];
}
I get the same warning with both of them and the view controller loads, but I cannot navigate back. I'm obviously doing something wrong, but I'm not sure what it is.
I think you want to push it instead of modally present it : presentViewController is a method from UIViewController to present modal VCs, whereas pushViewController pushes VCs in the navigation stack.
Try this instead :
[_navigationController pushViewController:anotherViewController animated:YES];
I'm a bit lost trying to figure it out...
I have a tab bar based app with login screen at the start. Login screen should be done as Modal View Controller BEFORE tab bar controller appears.
The problem is that I can present it only in viewDidAppear: method of TabBarController. And user can see for half a second content of the UITabBarController. I've tried to move call to viewDidLoad: or viewWillAppear: but it logs an error in console: "whose view is not in the window hierarchy!". As far as I can understand you can only add ModalViewController when all child UIViewControllers of UITabBarController are loaded, ad that happens in viewDidAppear: delegate method.
Do you have any solution how to show login screen without showing TabBarController before?
I've tried 2 ways of displaying ModalViewController, both of them work in viewDidAppear: only
XIB file with login view and using presentViewController: code
self.loginController = [[LoginViewController alloc] init];
[self presentViewController:self.loginController animated:NO completion:nil];
Storyboard, modal segue and calling it from the code:
[self performSegueWithIdentifier:#"loginScreen" sender:self];
Instead of a modal, you might consider pushing the login screen onto a navigation stack. Inside viewWillAppear: you can just instantiate your login viewController and push it. You could also do it in viewDidLoad if you'd like.
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController pushViewController:yourInstantiatedLoginViewController animated:NO];
}
In android, switching between activities, is fairly straightforward
you call
Intent intent = new Intent(this,NextActivity.class); <- define the next activity
startActivity(intent); <- start the next activity
finish(); < -get rid of the current activity
now in iOS i know how to do this:
UIViewController *nextviewcontroller = [[UIViewController alloc]initWithNibName:#"nextvc" bundle:nil];
[self presentViewcontroller:nextviewcontroller animated:YES completion:nil];
How do I get rid of the current view controller? so that currentviewcontroller dies after presenting nextviewcontroller ?
[self dismissViewController:YES]; doesnt seem to do the trick
the lifecycle methods viewWillDisappear and viewDidDisappear are called even if I don't call [self dismissViewController:YES];
i want "currentviewcontroller" to be removed from the memory, and from the viewcontroller stack, so that clicking "back" in "nextviewcontroller" will go to some thirdviewcontroller that was before currentviewcontroller
In iOS is different, since there's no concept of Activity and everything is more focused on the app itself (in Android you can mix activities from different apps). Therefore, there's no concept of "view controller stack".
The most similar concept is the "navigation stack" of navigation controllers, where you actually push and pop new view controller into some kind of linear navigation. A navigation bar is automatically created and populated with back buttons.
presentViewController will show your view controller modally upon the current one, but you can't thrash the presenting one since it's holding and containing ("defining context") the new one.
If you use a navigation controller for your navigation hierarchy (I don't know if you can), you can override the back button and use something like
UIViewController * prev = self.navigationController.viewControllers[self.navigationController.viewControllers.count -2 ]
[self.navigationController popToViewController:prev animated:YES]
With a modal view controller, you may try something like (I haven't tried but it may work)
[self.presentingViewController.navigationController popViewControllerAnimated:YES]
You should write one of these code into the target action of your close button.
iOS doesn't maintain a global stack of controllers in the way that Android does. Each app shows a controller at its root, and that one is responsible for showing the other controllers in the app. Controllers can display other controllers modally using presentViewcontroller:animated:completion: but the presenting controller remains underneath the presented one.
If your current controller is the root controller, then instead of using presentViewcontroller:animated:completion: you'd just do this:
self.view.window.rootViewController = nextViewController;
It's very common for the root controller to be a UINavigationController, which does manage a stack of controllers. If that is the case, and if your current controller is at the top of the stack, you'd do this:
[self.navigationController popViewControllerAnimated:NO];
[self.navigationController pushViewController:nextViewController animated:YES];
If your setup is different, you'd do something different; it's hard to say what without knowing more. But it's most likely that you'd be in the UINavigationController case.
In the viewDidAppear of your nextviewcontroller you could add :
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSArray *controllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray arrayWithArray:controllers];
[newViewControllers removeObjectAtIndex:[controllers count]-2];
self.navigationController.viewControllers = newViewControllers;
}
There is nothing available like this in iOS but you can achieve it doing something like below
NSArray *viewControllers=[self.navigationController viewControllers];
NSMutableArray *newControllers=[[NSMutableArray alloc] init];
for(int i=[viewControllers indexOfObject:self];i<viewControllers.count;i++){
[newControllers addObject:[viewControllers objectAtIndex:i]];
}
[self.navigationController setViewControllers:[[NSArray alloc] initWithArray:newControllers]];
I have tried the method of storing all the view controllers in an array but it didn't work for me . When you try popViewController it will move to the View Controller which is last in the stack.
You can make 2 navigation controllers and switch between them and also switch between the view controllers of a particular Navigation Controller.
For eg.
You can switch between 2 Navigation Controller using the following code:
FirstNavController *fisrtView=[storyboard instantiateViewControllerWithIdentifier:#"firstnavcontroller"];
self.window.rootViewController = firstView;
}else{
SecondNavController *secondView=[storyboard instantiateViewControllerWithIdentifier:#"loginnavcontroller"];
self.window.rootViewController = secondView;
}
If your FirstNavController has 2 ViewControllers then you can switch between them using pushViewController
SecondViewController *sc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondviewcontroller"];
[[self navigationController] pushViewController:sc animated:YES];
and popViewController
[self.navigationController popViewControllerAnimated:YES];
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];