I'm trying to switch tabs programmatically using:
[tabBarController setSelectedIndex:index]
I've also tried:
tabBarController.selectedViewController = [tabBarController.viewControllers objectAtIndex:index];
It works the first time - switching both tabs and the view associated with the tab. However it doesn't work the second time and thereafter. Then it erratically switches the tab (not always), and doesn't switch the view controller associated with the tab.
Any ideas?
Here's what I'm trying to accomplish:
Tab A: I have a tab that launches the camera to take a picture and add some details.
Tab B: I have a tab with a list of pictures taken and a bar button to add a new item by taking a picture and adding details.
I'm trying to make it so that when the user taps Tab A it switches to Tab B and launches the add item method. What's the best way to do this?
Here is more detailed code:
Tab A is hooked up to navigation controller with a UIViewController. In that controller I have the following:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate switchView];
}
In AppDelegate.m I have the following:
- (void)switchView
{
tabBarController = (UITabBarController *)self.window.rootViewController;
tabBarController.selectedViewController
= [tabBarController.viewControllers objectAtIndex:3];
}
The UIViewController class has a tabbarController property, so you can simplify things by calling the view controller's parent controller (the tab bar controller) instead of using the app delegate to access the tab bar controller. Calling setSelectedIndex should be fine too. So from inside your view controller:
[self.tabbarController setSelectedIndex:3];
To help with debugging, you could put some NSLogs in relevant places:
NSLog(#"The currently selected tab is: %#",self.tabbarController.selectedIndex);
It turns out the problem was that I was changing the tabBarController index from viewWillAppear instead of viewDidAppear. Must be something to do with the order in which things are loaded.
Related
In my iOS Project, there is a login ViewController that sends the user to a TabViewController if he has the right credentials.
This TabViewController has 5 tabs, the fifth one is for Logout, which send the user back to the Login ViewController, and of course clears out the already filled credentials of the user.
My problem is that i have the menu of the TableView shown in my Login page.
How to i get rid of this menu in my Login ViewController page ?
I use Xcode6 & Objective-C
if you need any further explanations/source code of my problem, feel free to ask.
note:
In the beginning, I mean when the Login ViewController is first shown to the user, the menu doesn't show.
It turns out that whole UI architecture of your app is based on the UITabBarController. However it is not very good practice in your case. I would like to suggest you add separate modal controller for presenting login page.
Try:
self.tabBar.hidden = YES;
Assuming that you are using a storybaord I have the given solution
I created a sample application and tried replicating your issue, so here's the look at my storyboard
The way i designed it i have a separate login view controller and two view controller (Menu List and Logout) which are embedded in a tabbar controller.
If you're new to storyboard then embedding viewControllers with tabbarController is pretty much straight forward, you select the view controller first and then go to the editors menu in Xcode
Alright now coming back to business, code which i added on the IBAction of the login screen button is given below where MainTabbar is the storyboardID of the TabMaster controller
AppDelegate *appdel = [UIApplication sharedApplication].delegate;
UIStoryboard *storyBoard = appdel.window.rootViewController.storyboard;
TabMasterController *tabController = [storyBoard instantiateViewControllerWithIdentifier:
#"MainTabbar"];
[appdel.window setRootViewController:tabController];
When i executed the application everything was OK and I was able to see the tabbar items after I hit the action button on login screen
Now its time to write some code for the logout tabbar item, so I selected the view controller assigned to the Main tabbar controller and added the delegate mentod of UITabbarController which looks some what like this
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
// Since i have two tabbar items 1 generally means logout in my case
if (tabBarController.selectedIndex ==1) {
AppDelegate *appdel = [UIApplication sharedApplication].delegate;
UIStoryboard *storyBoard = appdel.window.rootViewController.storyboard;
ViewController *tabController = [storyBoard instantiateViewControllerWithIdentifier:
#"LoginVC"];
[appdel.window setRootViewController:tabController];
}
}
LoginVC is the storyboardID of the Login View controller
After adding the above code when i used to tap on the logout tabbar item I was able to go back to the login screen in my storyboard.
Hope that helps.
Either you can add tab bar item programmatically or you can add in storyboard
If programmatically then add like
self.tabBarController.viewControllers = [NSArray arrayWithObjects:
[[UINavigationController alloc] initWithRootViewController:self.myContactsController],
[[UINavigationController alloc] initWithRootViewController:self.searchController],
[[UINavigationController alloc] initWithRootViewController:self.registrationController],
[[UINavigationController alloc] initWithRootViewController:self.loginController], nil];
Set tabbarController as initial view controller in storyboard
After checking login credential, On success
// to get list of current tab bar items
NSMutableArray *tbViewControllers = [NSMutableArray arrayWithArray:[self.tabBarController viewControllers]];
// to remove tab bar items using index value
[tbViewControllers removeObjectAtIndex:3];
[tbViewControllers removeObjectAtIndex:2];
// to add tab bar items
[tbViewControllers addObject:[[UINavigationController alloc] initWithRootViewController:self.myProfileController]];
[tbViewControllers addObject:[[UINavigationController alloc] initWithRootViewController:self.logoutController]];
// to set pre selected tab bar item
self.tabBarController.selectedIndex=2;
// set array items in tab bar
[self.tabBarController setViewControllers:tbViewControllers];
Fixed it, it's not the optimal solution but it worked for me:
what I did is :
1- I deleted the Tab Bar Item from my Login ViewController
2- I inserted a logout button in my table view screens
3- I added segues from my Table view screens to the login screen in my storyboard.
This way if the user clicks on the logout button , he will be directed to the login screen & cannot go back where he was unless he enters his credentials.
I've been looking this up for a while now, it might have a simple answer:
According to the Apple docs, past ios6, we can subclass UINavigationController. How do we perform a segue from identifier when it prevents anything that isn't a UINavigationController. Mainly:
uncaught exception 'NSGenericException', reason: 'Could not find a
navigation controllerfor segue 'profileSegue'. Push segues can only
be used when the source controller is managed by an instance of
UINavigationController.
I'm using JaSidePanels and my center panel (navigation) needed to be subclasses for a delegate as there is a menu on the left panel that I want to switch the views when clicked.
#interface CenterViewController : UINavigationController <MenuDelegate>
Basically, since this object is a CenterViewController at runtime, is there a way to cast it to its superclass? I've tried [self superclass] but that didn't work (same error).
I need to call this code in the CenterViewController. Is it possible to move it to UINavigationController?
- (void)viewDidLoad
{
RootViewController *rootViewController = (RootViewController *)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
MenuViewController *leftViewController = (MenuViewController *)rootViewController.leftPanel;
// Store a reference to the center view controller in the left view controller's delegate property
leftViewController.menuDelegate = self;
[super viewDidLoad];
}
- (void) MenuItemSelected: (NSString*) item
{
if ([item isEqualToString:#"Home"]) {
//These would throw the error since we're not technically a "UINavigationController"
//[self performSegueWithIdentifier: #"mapViewController" sender: nil];
} else if ([item isEqualToString:#"Profile"]) {
//[self performSegueWithIdentifier: #"profileSegue" sender: self];
}
}
EDIT:
More information with pictures.
I'm curious as to how navigation controllers should work with side panels. I've looked at many other plugins for sidepanels and here is an example. Even though it works, why does it have 2 nav controllers?
Here is our current setup:
So basically, am I thinking about this wrong in the sense that I want to push a new VC to the existing NavVC? Would it be better to push a new NavVC when a menu button is pressed? What would happen when we go into a subview from the Maps view. Would the menu be accessible via sliding?
If you look carefully at the message you will see that your problem isn't caused by subclassing UINavigationController it is because you are executing the segue against your (subclassed) UINavigationController. Push segues are executed against a UIViewController that is embedded in or managed by a UINavigationController, with the system then finding the managing UINavigationController via the view controller's navigationController property in order to execute the push.
The message you have received says
...Push segues can only
be used when the source controller is managed by an instance of
UINavigationController
In your case the source controller is an instance of UINavigationController, it isn't managed by a UINavigationController.
You haven't said exactly how your app navigation works, but I have a suspicion that a UINavigationController isn't the right tool to use anyway. If you are using the side menus to allow the user to select the central content in a random way (i.e. the user could select the first option then the fifth and then go back to the first) then you should probably just have a central view into which you present the selected view. Pushing views onto a UINavigationController will end up with a large 'stack' of views unless you pop the current view controller before pushing the new one, which is even more complicated and not the visual effect you are looking for.
You can still achieve the 'push' style transition but you will need to use a custom segue. If it were me I would probably push from the left if the user selected a menu item that was closer to the top than the current option and from the right if the new item was closer to the bottom than the current, but again I am making assumptions on how your app navigation works.
UPDATE
Yes, I think you are on the right track with the section of your updated question. Navigation controllers are for navigating a series of related views in a hierarchical manner - think of the Settings app - you select "general" or "wall paper" or whatever - each of these then has a series of views that you can navigate through; up and down a stack.
In your app it looks like home, profile and settings should each be navigation controllers. Your root view would then just be a view. Your menu would select which view controller to present in the root view - this is like a tab bar controller, except your menu takes the place of a tab bar.
You can allocate your home, profile & settings view controllers in your appDelegate and store them to properties of the appDelegate. Then you can use something like this:
- (void) MenuItemSelected: (NSString*) item
{
myappDelegate *app=(myappDelegate *)[UIApplication sharedApplication].delegate;
[delegate.currentViewController removeFromParentViewController];
UIViewController *newController;
if ([item isEqualToString:#"Home"]) {
newController=app.homeViewController;
} else if ([item isEqualToString:#"Profile"]) {
newController=app.profileViewController;
}
if (app.currentViewController != newController)
{
[app.currentViewController removeFromParentViewController];
[app.rootViewController addChildViewController:newController];
app.currentViewController = newController;
}
I've been struggling this problem for several days. In my application I have tab bar controller with UINavigationViewControllers inside. I want every navigation bar in every navigation controller to look exactly the same depending on user actions and app state.
For example: if user logged in to my app in first view controller, app sets his name in navigation bar and sets navigation bar into logged in state. Then when user selects other tab item, I want to set this logged in state of navigation bar from first view controller to other view controllers. I've tried to use singleton but with no effect.
Seems like you have to do 2 things:
Set the current settings while init the UIViewController
Update all initiated Controllers after state changes
to 1:
create a class with
#interface UIViewController (UINavigationController)
- (UINavigationController*)wrapWithNavigationController;
#end
#implementation UIViewController (UINavigationController)
- (UINavigationController*)wrapWithNavigationController
{
UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:self];
// your customizations
navigationController.navigationBar.barStyle = UIBarStyleBlack;
[...]
return [navigationController autorelease];
}
#end
you can call it UIViewControllerAdditions for example.
to 2:
use the NSNotificationCenter for updates :)
Do you really have multiple UINavigationControllers, or do you just want to use the navigationBar for the same actions and functionality in every tab?
If you just want to have the bar, you could also use an UINavigationController as your apps rootViewController and then the UITabBarController as a childViewController of your UINavigationController.
i have a navigation controller which is my application's root view controller. it is my main screen. I have a split view controller whose master view lists location and detail view shows the location on the map. I wanted to push the split view controller to the navigation controller, but it throw a error saying split view controller cant be pushed to navigation controller and it must be application's root view controller.
So i tried a hard way.
MyAppDelegate *myappdelegate= [[UIApplication sharedApplication] delegate];
UISplitViewController * vc= [[UISplitViewController alloc] init];
vs.viewControllers = [NSArray arraywithObjects......
....
myappdelegate.window.rootViewController= vc;
This works. It shows split view controllers without animation as expected. And i do the same steps when i was closing split view controller. i am creating a navigation controller with main screen nib and setting this my app delegate's window.rootviewController again.
And it succesfully loads main screen again.
But i suspect that this is the proper way of achieving this. Is there more eligible way of doing this?
And i couldnt release split view controller's child controllers. i made a breakpoint on my release method of child controllers. it wasnt be catched. I assumed that when i set my app's root view controller as navigation controller, the old root view controller (split view controller) must be released along with its childs.
Then i tried below code when closing split view controller.
UISplitViewController *oldviewcontroller= (UISplitViewController*) myappdelegate.window.rootViewController;
for (UIViewController *child in oldviewcontroller.viewControllers)
{
[child release];
}
//Setting the navigation controller as window.rootviewController again in here.
This code throw an error saying "[UIImageView __viewDidDisappear:]: unrecognized selector sent to instance 0x7d...."
i think because of releasing the view already, there is no imageview on viewdidDisappear event.
In brief, my question is that am i using right method to achieve this? If so, how can i successfully release all child view controllers?
I finally found a way. I probably found the error. What i have done is cutting the branch on which i am sitting. I was releasing the view controller which i am currently in:) When viewdidDisappear called, there is no such view controller. Because i throw away it to space already.Below is my working steps. I hope it will be useful to someone. But i cant stand thinking of apple may reject my app. I wish finding a suitable way.
This is my working ultimate way of using split view controller and navigation controller as window root view controller alternately.
Firstly i defined NavigationController and SplitViewController property in AppDelegate interface.
AppDelegate.h
#property (assign,nonatomic) UINavigationController * NC;
#property (assign,nonatomic) UISplitViewController *SVC;
Secondly i assign newly created NC on AppDelegate didFinishLaunch event.
AppDelegate.m
//Creating my main screen controller
//Creating my navigation controller with my view controller instance. Then
self.NC= my_navigation_controller;
self.window.rootViewController= self.NC;
Thirdly creating a splitview controller and setting as app's root view controller
MyMainScreen.m
-(void) OpenSplit()
{
//Creating my master view controller of SVC
//Creating my detail view controller of SVC
//Creating my SVC;
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.SVC= newly created my SVC;
app_delegate.window.rootViewController= app_delegate.SVC;
}
Fourthly releasing unused NC in viewDidLoad event of detail view of SVC.
MyDetailView.m
- (void) viewDidLoad()
{
...
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.NC= nil; //i dont need it now. i am releasing. Releasing Navigation Controller release as well child controllers. I learned with testing.
}
Fifthly managing close split view function.I used UIBarButton on NavigationBar in DetailView.
MyDetailView.m
-(void) closeSplitView
{
//Creating navigation controller with my main screen view controller
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.NC= newly_created_NC;
app_delegate.window.rootViewController= appdelegate.NC;
}
Sixthly handling unused split view controller in Main Screen viewDidLoad event.
MyMainScreen.m
-(void) viewDidLoad
{
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.SVC= nil; //I am releasing it because i am working with NC now.
}
Im using the new storyboarding features with ios. I have a tab bar which also has a class name called mytabbarcontroller. However I do not have a seperate class for this as in a .h/.m file for this since all its implementation is in different views which is sorted. Now i have a view controller which appears before the tab bar with an activity indicator. Once it stops it has to move to the tab bar controller. However i cant initialise it with mytabbarcontroller since i dont have .h files to import. Currently it goes to a tab bar controller with nothing on it - basically it creates a new tab bar controller. My code is as follow. I also tried initwithnibname and gave the nib name - i.e mytabbarcontroller. However this throws an error.
UITabBarController *controller = (UITabBarController *)[[UITabBarController alloc] init ];
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
because you are declaring a new UITabBarController, try this instead, import and intialize the first view of the tab bar, for example
FirstSceneController.h
import "FirstTabViewController.h"
...
FirstTabViewController *firstTab = [self.storyboard instantiateViewControllerWithIdentifier:#"mytabbarcontroller"];
[firstTab setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentModalViewController:firstTab animated:YES];
....
and also be sure to add a navigation controller and connect it to your first scene.