I've built my app with storyboards and all views are managed by a tabbarcontroller.
So on launch (I'm only working on the iPad UI currently) it does this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UISplitViewController *splitViewController = [tabBarController.viewControllers objectAtIndex:0];
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
UINavigationController *masterNavigationController = [splitViewController.viewControllers objectAtIndex:0];
ProductionMasterViewController *controller = (ProductionMasterViewController *)masterNavigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
}
}
I want to be able to enable or disable the tabs in the tabBarController based on user input (so, for example, an item needs to be selected in the first tab in order to access the second and third tabs, which are disabled by default)
What I'm not clear on is how to access the tabs in order to enable/disble them. Would I create an instance of the appdelegate and then do something like
AppDelegate *d = (AppDelegate *)[[UIApplication sharedApplication] delegate];
UITabBarController *tabs = (UITabBarController *)[d.window rootViewController];
[[[[tabs tabBar] items] objectAtIndex:2] setEnabled:YES];
[[[[tabs tabBar] items] objectAtIndex:3] setEnabled:YES];
[[[[tabs tabBar] items] objectAtIndex:4] setEnabled:YES];
(That kinda seems like it should work but it also seems fairly gross.)
Since you're using a Storyboard based app, I'd assume you have the UITabBarController defined in the storyboard as the root controller. Incidentally, you can also retrieve it by identifier instead of walking from the window to the root view controller.
Restricting which tabs are selectable, is achieved by setting a delegate of the UITabBarController (i.e. one that conforms to UITabBarControllerDelegate).
In the delegate, you can implement these two methods:
– tabBarController:shouldSelectViewController:
– tabBarController:didSelectViewController:
Likely, you just need the first to restrict (inhibit) selection, until your workflow is ready.
Another approach, is to set the "viewControllers' property on the tab bar controller, each time a milestone is passed. At each milestone, you set a more expansive array of view controllers into this property, which will open up the selection of the additional tab item.
SWIFT 3
(expanded for ease of understanding)
let arrayOfTabBarItems = tabBarController?.tabBar.items
if let barItems = arrayOfTabBarItems, barItems.count > 0 {
os_log("barItems.count is now ", barItems.count)
tabBarItem0 = barItems[0]
tabBarItem0.isEnabled = true
tabBarItem1 = barItems[1]
tabBarItem1.isEnabled = true
tabBarItem2 = barItems[2]
tabBarItem2.isEnabled = true
tabBarItem3 = barItems[3]
tabBarItem3.isEnabled = true
tabBarItem4 = barItems[4]
tabBarItem4.isEnabled = true
}
This can be used in your viewWillAppear on each tab controller. Check your rules against this and restrict each tab accordingly.
(more concise method)
let arrayOfAllTabBarItems = tabBarController?.viewControllers
if let tabBarArray = arrayOfAllTabBarItems, tabBarArray.count > 0 {
for x in 0...tabBarArray.count-1 {
let tabBarItem = tabBarArray[x]
if tabBarItem.title != nil {
if tabBarItem.title == "Tab1" || tabBarItem.title == "MyTab" || tabBarItem.title == "Tab2Check" {
tabBarItem.tabBarItem.isEnabled = !(isMyRuleTrue!)
}
}
}
}
Related
I want to load SFSafariViewController inside of a tab, so the tab bar is at the bottom of the entire Safari view.
Is this possible? I tried this with no luck:
[self.tabBarController presentViewController:sfController animated:YES completion:nil];
Is it required that the Safari view be full screen?
I was able to achieve this programmatically. They key to not have the UITabBar overlay on top of your UIViewController is to set translucent to NO:
In your AppDelegate.m:
#import SafariServices;
// ...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
tabBarController.tabBar.translucent = NO;
SFSafariViewController *firstVC = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:#"http://stackoverflow.com"]];
firstVC.title = #"SFSafariViewController";
UIViewController *secondVC = [[UIViewController alloc] init];
secondVC.view.backgroundColor = [UIColor blueColor];
secondVC.title = #"Blue VC";
tabBarController.viewControllers = #[firstVC, secondVC];
self.window.rootViewController = tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
Using JAL's answer as a base, I was able to implement this myself in an app that had existing structure with tabs already.
I wanted tab #3 to go into a Safari controller within the tab after a button was pressed on the existing view, and not pop the Safari controller into its own window like it does using Apple's default code.
The key was to swap in a SFSafariViewController into the existing UITabBarController's array of view controllers. I saved the existing original view controller on tab #3 (index 2) to come back to it when the Done button was pressed on the Safari controller.
Here's what I did to go into the Safari controller from with my tab when a button was pressed:
NSMutableArray *viewArray = [NSMutableArray arrayWithArray:self.tabBarController.viewControllers];
self.savedController = [viewArray objectAtIndex:2];
[viewArray replaceObjectAtIndex:2 withObject:safariController];
self.tabBarController.viewControllers = viewArray;
[self setTabIconTitle];
Then I could swap back to the original view on that tab like this when the Done button was pressed on the Safari controller using this delegate call:
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
NSMutableArray *viewArray = [NSMutableArray arrayWithArray:tabBarController.viewControllers];
[viewArray replaceObjectAtIndex:2 withObject:self.savedController];
tabBarController.viewControllers = viewArray;
[self setTabIconTitle];
}
When I swapped controllers in an out of the tabBarController view controller array, I lost my tab icon and tab name, so I had to set those. Here is how I fixed that issue (and kept my theming when the tab icon was touched):
- (void)setTabIconTitle
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UITabBar *tabBar = tabBarController.tabBar;
UITabBarItem *marketplaceTab = [tabBar.items objectAtIndex:2];
marketplaceTab.image = [[UIImage imageNamed:#"tab2-icon"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
marketplaceTab.selectedImage = [UIImage imageNamed:#"tab2-icon"];
marketplaceTab.title = #"My Tab";
}
I must admit that I am not sure that Apple intended that the SFSafariViewController be used in this way within a tab, based on what the normal behavior of calling SFSafariViewController currently does. Just be aware that future iOS updates may change this behavior and always test your code when new iOS versions go into Beta.
I know this question has been asked many times. But I am a newbie and I was not able to find any solutions to help me in my condition. Here is a brief explanation:
I have an app with a UITabBarController as root.
Inside the app delegate I check if user is already logged in. If yes I will open root controller.
self.window.rootViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
My application is working fine. But now I need to implement notification. When I get notification content inside didFinishLaunchingWithOptions:
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
NSString *messageType = [notificationPayload objectForKey:#"messageType"];
Now if there is a message:
if (messageType.length > 0 )
{
//Here based on message I need to open different tabs of TabBarViewController
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
//RootTabBarViewController *listingVC = [[RootTabBarViewController alloc] init];
[(UINavigationController *)self.window.rootViewController pushViewController:tabBarController animated:YES];
}
This piece of code above did not work for me.
And also I don't know how to open different tabs and give value to their badges form here. It always navigate to the first index tab with these code. I have seen other answers say:
self.tabBarController.selectedIndex = 2;
But did not work for me. I have implemented an UITabBarController class and I can set value for the each tab item badges from there but I get my notificationPayLoad in AppDelegate.
As you told that you are using TabBarController with storyboard. Then why are you initialising again? You can just do as following
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if([[NSUserDefaults standardUserDefaults]boolForKey:#"Selected"])
{
[[NSUserDefaults standardUserDefaults]setBool:NO forKey:#"Selected"];
UITabBarController *tabController = (UITabBarController*)self.window.rootViewController;
tabController.selectedIndex=1;
}else{
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:#"Selected"];
UITabBarController *tabController = (UITabBarController*)self.window.rootViewController;
tabController.selectedIndex=0;
}
return YES;
}
Here I am just showing a demo code for accessing TabbarController at different selectedIndex within didFinishLaunching.
Assuming that you have designed(drag n dropped UITabBar/ UITabBarController) in storyboard.
if (messageType.length > 0 )
{
//Here based on message I need to open different tabs of TabBarViewController
NSUInteger indexOfRequiredTab = 2;// pass the index of controller here
id rootObject = self.window.rootViewController;
if ([rootObject isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBarControllerObject = (UITabBarController *)rootObject;
if (indexOfRequiredTab != NSNotFound && indexOfRequiredTab < tabBarControllerObject.tabBar.items.count) {
tabBarControllerObject.tabBar.items[indexOfRequiredTab].badgeValue = #"2"; //to set badge value
tabBarControllerObject.selectedIndex = indexOfRequiredTab; //Setting this property changes the selected view controller to the one at the given index in the viewControllers array
}
}
}
Views designed on story board are already initialized, we can access them using the IBOutlet and change their properties.
I have a view controller say 'HomeViewController' on which I have added a UITabBarController, which has 4 UITabBarItems. Now what I want is, when I select the HomeViewController no tab should should be selected for the first time and another view should be loaded. I have tried UITabBar instead of UITabBarController but its not working. I have also tried :
tabBarController.selectedViewController=nil; //giving crash
tabBarController.selectedIndex=-1; //not working either
UITabBarItems should be selected when user will click on them.
You have to add UITabBar object on HomeViewController.
In ViewDidLoad you have to add following code:
ViewController1 *controller1 = [[ViewController1 alloc] initWithNibName:#"ViewController1" bundle:nil];
ViewController2 *controller2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
viewControllersArray = [[NSArray alloc] initWithObjects:controller1,controller2, nil];
tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = [[NSArray alloc] initWithArray:viewControllersArray];
for(int i = 0; i < tabBarController.tabBar.items.count; i++){
[[tabBarController.tabBar.items objectAtIndex:i] setTitle:[[tabBar.items objectAtIndex:i] title]];
}
isItemSelected = NO;
Also set the tag of tab bar items starting from 0. As 0 for first item, 1 for second item and so on.
and implement delegate method:
(void)tabBar:(UITabBar *)tabBar1 didSelectItem:(UITabBarItem *)item{
if(!isItemSelected){
isItemSelected = YES;
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
appDel.window.rootViewController = tabBarController;
}
tabBarController.selectedIndex = item.tag;
tabBarController.selectedViewController = [viewControllersArray objectAtIndex:item.tag];
}
I think this is not the right way, as Concept of creating TabBar says that " Show the particular controller of related tab". If u do not select any tab than what else you will show on screen, "A black screen ?". so please re-architecture your design.
I'm having trouble calling a newly created view controller within the storyboard. I have created an app using the default master-detail template. The only simple thing I'd like to do is call a new View Controller window when a specific check is valid/invalid (depending on requirement) (I refuse to use the term "Login" :-) ).
What is the best location to do this? In here (appDelegate)?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
UINavigationController *masterNavigationController = splitViewController.viewControllers[0];
MasterViewController *controller = (MasterViewController *)masterNavigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
} else {
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
MasterViewController *controller = (MasterViewController *)navigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
}
//Should I call the new view controller object from my storyboard at this location? (i.e. myView)
return YES;
}
I'm even considering starting all over without using the template of Xcode.
Do you need to use different controllers depending on check result?
If yes than there are few suggestions. You can cover your view with some activity view which covers all screen, wait for checking and than remove activity view.
Another thing you can try is making another view controller in storyboard and move starting arrow to it. You can try to login and after it perform correct segue.
And at least i can suggest to create root view controller programmatically.
something like this.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
levelViewController = [[LevelViewController alloc] init];
window.rootViewController = levelViewController;
[window makeKeyAndVisible];
}
I am relatively new to iOS, hence I apologize for any inconsistency in my question. I need help with the following issue with an app I'm trying to build. My issue is this: The app i am working has a navigation based functionality with a tableview(daily filled by user) and a detailed tableview listing the inputs of the user, but this is just one functionality of the app.
I want to have a main tab based view where one of the tabs(each tab representing a functionality) points to this module.
I wanted to ask for steps and changes i need to make to for example app delegate or rootviewcontroller(I can post the code if it helps better) to make is so that the app starts with a mutli-tabbed bar view where one tab refers to view linked to the rootviewontroller of the navigation-based app.
For summary: Need a main tab bar view where one tab points to the rootviewcontroller highlighted in the screenshot(link below)
If helpful here is a relevant function code i have in app delegate :
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
RootViewController *rootViewController = (RootViewController *)[[navigationController viewControllers] objectAtIndex:0];
rootViewController.managedObjectContext = self.managedObjectContext;
//Next TWO LINES FOR COLOR BACKGROUND
[[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundColor:[UIColor redColor]];
}
PS:Here is the screenshot for the storyboard: where i would like to have one tab refer to the view(highlighted in the screenshot) which is linked class rootviewcontroller.m/h
The screenshot: http://i.stack.imgur.com/G9AXI.png
edit: The actual question can be seen as: How and what do i need to do to have a tabbarviewcontroller which i would add with storyboard become my rootviewcontroller instead of the navigationcontroller(highlighted in black in the screenshot: http://i.stack.imgur.com/G9AXI.png).
My current rootviewcontroller.m manages anything related to the tableview of the current navigationviewcontroller, do i need to change that also?.
I apologize for excessiv details, I am really new to iOS dev.
From this one http://i.stack.imgur.com/suLBm.png I tried to embedd in tab barviewcontrol only with storyboard to this one http://i.stack.imgur.com/TZxLo.png I tried to embedd in a tab controller just by story but i get an error :'NSInvalidArgumentException', reason: '-[UIViewController setManagedObjectContext:]: unrecognized selector sent to instance 0x8184e30'
classes related to this are(especially rootviewcontroller.m which is a navigationcontroller for now:
AppDelegate.{h,m}
Configures the Core Data stack and the first view controllers.
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
RootViewController *rootViewController = (RootViewController *)[[navigationController viewControllers] objectAtIndex:0];
rootViewController.managedObjectContext = self.managedObjectContext;
}
RootViewController.{h,m}
Manages a table view for listing all values entered. Provides controls for adding and removing these values.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
DetailViewController.{h,m}
Manages a detail display for display details of each entered value.
My initial guess is that i need to change the rootviewcontroller appdidfinishlaunching.
Any suggestions ?
In fact now you have:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UITabBarController *tabController = (UITabBarController *)self.window.rootViewController;
RootViewController *rootViewController = (RootViewController *)[[[[tabController viewControllers] objectAtIndex:0] viewControllers] objectAtIndex:0];
rootViewController.managedObjectContext = self.managedObjectContext;
}
So you actually need a UITabBarViewController in the Storyboard and you can point to the UINavigationController if you want the ability to push other controllers.
You don't need other UINavigationControllers as I saw in your screenshot, as long as the rootviewcontroller is an UINavigationController.
You can add the UINavigationController as first of the tabs and then you can go and fill the other tabs with the viewcontrollers that you need displayed.
SO basically you need to create UITabBarController as rootviewcontroller.
Let me know if I understood your question correctly.
Here is an example of UITabBarController :
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
//Here you set your controller
UIViewController* centerController = [[UIViewController alloc]init];
UINavigationController *navCenter = [[[UINavigationController alloc] initWithRootViewController:centerController] autorelease];
UITabBarController *tabBarController = [[[UITabBarController alloc] init] autorelease];
tabBarController.viewControllers = [NSArray arrayWithObjects:navCenter,nil];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.window.rootViewController = tabBarController;
return YES;
}
Let me know if it worked.
You should have something like this :