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.
Related
I try to push a UIViewController onto a UINavigationController. The NavigationBar changes (i.e. a back-button appears) but the view is not pushed (*).
I have a UITabBarController as my applications RootViewController.
When I switch to another tab and then switches back, the view (*) gets pushed.
I have never seen this behaviour before. My problem is exactly the same as this, however the methods that solved that issue did not solve mine.
Initially
After I press the row
I understand that this question might be related to issues in AppDelegate, therefore i post the code I use.
Code:
in AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[MagicalRecord setupCoreDataStackWithStoreNamed:#"DBModel"];
/* CONTACTS LIST CONTROLLER */
BoonContactListViewController *contactListViewController = [[BoonContactListViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController *contactListNavigationController = [[UINavigationController alloc] initWithRootViewController:contactListViewController];
[contactListNavigationController setValue:[[BoonNavigationBar alloc]init] forKeyPath:#"navigationBar"];
contactListNavigationController.tabBarItem.title = [NSLocalizedString(#"CONTACTS", nil) capitalizedString];
contactListNavigationController.tabBarItem.image = [UIImage imageNamed:#"menu_contacts.png"];
/* INVITATIONS */
BoonInvitationListViewController *invitationListController = [[BoonInvitationListViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController *invitationNavigationController = [[UINavigationController alloc] initWithRootViewController:invitationListController];
[invitationNavigationController setValue:[[BoonNavigationBar alloc]init] forKeyPath:#"navigationBar"];
invitationNavigationController.tabBarItem.title = [NSLocalizedString(#"SETTINGS", nil) capitalizedString];
invitationNavigationController.tabBarItem.image = [UIImage imageNamed:#"menu_invitations.png"];
/* SETTINGS */
BoonSettingsViewController *settingsViewController = [[BoonSettingsViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController *settingsNavigationController = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
[settingsNavigationController setValue:[[BoonNavigationBar alloc]init] forKeyPath:#"navigationBar"];
settingsNavigationController.tabBarItem.title = [NSLocalizedString(#"SETTINGS", nil) capitalizedString];
settingsNavigationController.tabBarItem.image = [UIImage imageNamed:#"menu_settings.png"];
/* TAB BAR */
BoonTabBarViewController *tabBarController = [[BoonTabBarViewController alloc] init];
tabBarController.viewControllers = #[contactListNavigationController, invitationNavigationController, settingsNavigationController];
[self.window setRootViewController:tabBarController];
[self.window makeKeyAndVisible];
[tabBarController showLogin];
return YES;
}
EDIT:
In the viewController that i am trying to push, neither viewWillAppear, viewDidLoad nor viewDidAppear is called.
If I use presentViewController: animated: completion: I get the preferred behaviour, id rather not though
EDIT 2
How I push my new VC
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BoonContactInfoViewController *contactInfoViewController = [[BoonContactInfoViewController alloc] initWithNibName:nil bundle:nil];
NSLog(#"NAV %#", self.navigationController);
[self.navigationController pushViewController:contactInfoViewController animated:YES];
}
EDIT 3
It is only the initial tab that cannot push ... if i swap places of the first and second tab, i can push a view controller using in the way i do above.
EDIT 4
It works if i (in my tabBarController) calls
self.selectedIndex = 1;
self.selectedIndex = 0;
EDIT 5
- (void)showLogin
{
if([BoonUserHandler getLogin].length > 0 && [BoonUserHandler getPassword].length > 0){
return;
}
BoonWelcomeViewController *welcomeWC = [[BoonWelcomeViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController *welcomeNavigationController = [[UINavigationController alloc] initWithRootViewController:welcomeWC];
[welcomeNavigationController setNavigationBarHidden:YES];
[self presentViewController:welcomeNavigationController animated:NO completion:nil];
}
What version of iOS are you developing for?
I'd first ask why you're hacking in a nav bar using:
[settingsNavigationController setValue:[[BoonNavigationBar alloc]init] forKeyPath:#"navigationBar"];
rather than the iOS5+ UINavigationController method:
- (instancetype)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass
But my overall suggestion would be to remove all this code and use a storyboard. This looks like the perfect opportunity.
I think you are getting wrong Navigation controller to push that's why it showing this problem..
You have to fetch right navigation controller from tab controller
self.tabBarController.selectedIndex = 0;
just change tab controller selected index
I am a newbie with regards to IOS Development, so I really need help regarding this matter. I have been working in a program where in a have a slide bar as well as a tab bar. However, when I click an item from the slide bar, it goes to the new view and the tab bar disappears. I tried embedding the new view into a navigation controller and adding a push segue between my tab bar controller a to the new view.. but still, tab bar won't show up.
What will I do to retain or use my existing tab bar in my new view? so, it will be visible to the new view.
Thanks! your help is very much appreciated. :)
First, you gotta perform a push, not a modal. Then, you could use this method hidesbotTombarWhenPushed in the view controller that pushes, that should do the trick.
If not, maybe you can clarify more or post some code :)
This may be because of you are not adding tabview controller as the window's root view controller in AppDelegate. Check the sample code below
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
searchController = [[SearchController alloc]initWithNibName:#"SearchController" bundle:nil];
navCtrl1 = [[UINavigationController alloc]initWithRootViewController:viewController];
navCtrl2 = [[UINavigationController alloc]initWithRootViewController:searchController];
NSMutableArray *viewControllersArray = [[NSMutableArray alloc]init];
[viewControllersArray addObject:navCtrl1];
[viewControllersArray addObject:navCtrl2];
tabController = [[UITabBarController alloc]init];
tabController.viewControllers = [NSArray arrayWithObjects: navCtrl1 ,navCtrl2,nil];
self.window.rootViewController = tabController;
[self.window makeKeyAndVisible];
return YES;
}
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 :
I've had a good look at the Apple docs as well as similar Stack Overflow questions, but I am stuck on why my navigationController is null when using tab bars. I am trying to build most of the app from code, and am not using XIBs to insert a navigationController.
While debugging I have greatly simplified my app, down to two tabs. One tab holds a tableview and when a row is touched I'm expecting a detail page (from a XIB) to appear. Should be pretty simple. I am finding the value of self.navigationController is NULL when attempting to push the detail view, and of course it is then not working. I took the tab bar our completely and it works fine from a single view (the tableview). In this instance self.navigationController has a value.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// With Tab Bars
self.tabBarController = [[UITabBarController alloc] init];
ViewController *vc1 = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
vc1.tabBarItem.title = #"Words";
vc1.tabBarItem.image = [UIImage imageNamed:#"tab_feed.png"];
TextTableViewController *vc2 = [[TextTableViewController alloc] init];
vc2.tabBarItem.title = #"Text";
vc2.tabBarItem.image = [UIImage imageNamed:#"tab_live.png"];
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc1];
NSArray* controllers = [NSArray arrayWithObjects:vc2, navController, nil];
tabBarController.viewControllers = controllers;
tabBarController.delegate = self;
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
window.rootViewController = self.tabBarController;
[window makeKeyAndVisible];
return YES;
}
From TextTableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
TextViewController *detailViewController = [[TextViewController alloc] initWithNibName:#"TextViewController" bundle:nil];
Text *text = [[Text alloc] init];
text = [textArray objectAtIndex:indexPath.row];
detailViewController.TextID = text.textID;
NSLog(#"Nav Controller: %#",self.navigationController);
[self.navigationController pushViewController:detailViewController animated:YES];
NSLog(#"pushed");
}
I've also got two questions related to this problem.
(1) What is the purpose of this line. It doesn't appear to make a difference if it is in or out, and is absent from the Apple example.
tabBarController.delegate = self;
(2) When creating an array of tabs, one of the views is made the navigationController. Does it matter which tab it is or should this be a different view altogether not related to any tab and not visible. Is this where the problem lies?
In answer to question (1) about the tab bar controller's delegate, see the UITabBarControllerDelegate protocol reference. For the basic functionality of a tab bar controller, you don't need to bother with a delegate.
But let's say you want to do something special--for instance, save a document or reset an interface element to a default value--when the user changes tabs. You could make one of your classes, perhaps your app delegate or another controller class, conform to the UITabBarControllerDelegate protocol and implement tabBarController:didSelectViewController:
In your "answer" you asked if each tab will need its own UINavigation controller. That is absolutely correct. Basically, each tab is a completely independent hierarchy, so you need a separate UINavigation controller in each tab that requires one.
This should also imply the answer to your question (2) in the original post. You need to add the nav controller to the specific tab(s) that needs it.
OK I found it. The UINavigationController needs to be contained within the appropriate tab of the UITabBarController. So by making this coding change (below), a new UINavigationController is embedded in the tab with the tableview.
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc2];
NSArray* controllers = [NSArray arrayWithObjects:vc1, navController, nil];
Which then begs the question: what if you need multiple examples of this - do you create a new UINavigationController for each tab that has a need for one, and mark each one as a rootViewController?
I have a problem I can't figure out, I have made an application which uses UIsplitview inside a tab bar. I have been implementing the different tabs however now when I am working on the first tab - the UIsplitview is not aligned in landscape mode. Do you guys have any suggestions - if I start it in portrait and go to landscape, then there's no problem at all.
Update:
I dont do any init with frames anywhere, and I have checked the sizes etc. in IB. The following shows how I add the uisplitview controller in the app delegate. It has been done this way because I wanted a splitview in a tabbar controller. When i have added the spilview I just set the master and detail view in IB. A bit of mystery.
if (index == 2) {
detailViewController = [[DetailUserCreatorViewController alloc] initWithNibName:#"DetailUserCreatorView" bundle:nil];
userContent=[[UserContentForPopViewController alloc]init];
userContent.userDetails=detailViewController;
detailViewController.delegate=userContent;
//rootViewController.navigationItem.title = #"List";
UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:userContent] autorelease];
splitViewController = [[UISplitViewController alloc] init];
splitViewController.tabBarItem = controller.tabBarItem;
splitViewController.viewControllers = [NSArray arrayWithObjects:nav, detailViewController, nil];
splitViewController.delegate = detailViewController;
[controllers replaceObjectAtIndex:index withObject:splitViewController];
}
Update: I tried to set the selected tab in application didfinishlaunch in the app delegate - self.tabBarController.selectedIndex = 0; and this made the tab start at the correct placement. However it does not seem to be a proper solution.
Some pointers...splitViewController needs to be added as a subview of window:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
return YES;
}
The following code is incorrect. You should not assign a viewController to a delegate.
splitViewController.delegate = detailViewController;
You will also not require this line of code:
[controllers replaceObjectAtIndex:index withObject:splitViewController];
The following line handles that part of assigning delegates.
splitViewController.viewControllers = [NSArray arrayWithObjects:nav, detailViewController, nil];
Also, if you can upload your code, I'll try to correct it and post back the reason and corrected code...