I am creating an iPad app using XCode 4 and Storyboards. I have a TabBar Controller with two UIViews. I have my iPad app and Kal in a workspace. I am trying to copy some of the Kal sample code to get the calendar to display. This is what I have copied (with minimal changes):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// initialization...
KalViewController *kal = [[KalViewController alloc] init];
kal.title = #"Saori";
// configuration
kal.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Today" style:UIBarButtonItemStyleBordered target:self action:#selector(showAndSelectToday)];
kal.delegate = self;
EventKitDataSource *dataSource = [[EventKitDataSource alloc] init];
kal.dataSource = dataSource;
// Setup the navigation stack and display it.
navController = [[UINavigationController alloc] initWithRootViewController:kal];
[window addSubview:navController.view];
[window makeKeyAndVisible];
return YES;
}
On the last 3 lines (Setup the navigation stack and display it) I am getting errors such as undeclared "navController", and "window". I don't believe the 3 lines belong in this app because I have the TabBar Controller, but I don't know what to replace them with to display the calendar.
How do I take my existing code and display the Kal calendar in the TabBar Controller's UIView?
You are right that you don't need the bottom 3 lines, but in fact, you don't need any of these lines. If you're using StoryBoards you should be able to setup your view stack entirely from there. The only thing you might need is to add the EventKit datasource to your Kal view controller, but you can do that from [KalViewController viewDidLoad:]. Your view stack should look like this:
Window -> Tab Bar Controller -> Nav Bar Controller -> Root View Controller
So by adding your Nav Bar view controller directly to the window, you're skipping the Tab bar controller entirely.
Related
I'm having trouble accessing my view controllers under the tab bar controller. Here is what my storyboard looks like:
View Controller A (-> Page View Controller -> View Controller C
View Controller A -> Tab Bar Controller (MyTabBarController.h/.m) -> Navigation Controller (MyNavigationController.h/.m)-> View Controller B (TabViewController.h/.m)
Tab Bar Controller (MyTabBarController.h/.m) -> View Controller D
Tab Bar Controller (MyTabBarController.h/.m) -> View Controller E
From View Controller A I have an IBAction called loginButton that is connected to the Tab Bar Controller, and currently it looks like this:
- (IBAction)loginButton:(id)sender {
MyNavigationController *localNavigationController;
UIStoryboard * storyboard = self.storyboard;
MyTabBarController *tbc = [[MyTabBarController alloc] init];
NSMutableArray *localControllersArray = [[NSMutableArray alloc] initWithCapacity:1];
TabViewController *login = [storyboard instantiateViewControllerWithIdentifier: # "TabViewController"];
localNavigationController = [[UINavigationController alloc] initWithRootViewController:login];
localNavigationController.delegate = self;
[localControllersArray addObject:localNavigationController];
tbc.viewControllers = localControllersArray;
tbc.delegate = self;
tbc.moreNavigationController.delegate = self;
tbc.selectedIndex = 0;
[self presentViewController:tbc animated:YES completion:^{
}];
}
I'm not able to get this displayed correctly. I am getting a bunch of warnings in this piece of code. and it is also not showing the different tab items in the bottom of the Tab Bar, even though I have put images/text on each tab.
So how do I display/access the view controllers inside the Tab Bar Controller correctly? (ie View Controllers C/D/E)?
The storyboard that you show in your question already contains the tab bar controller, navigation controller, and login controller properly hooked up to each other. Because of that, you shouldn't be instantiating a new tab bar controller or navigation controller in code -- they will be instantiated by the storyboard when you instantiate the tab bar controller. So, the only thing you need to do, is to give the tab bar controller in the storyboard an identifier, and do this (assume the identifier is called MyTabBarController):
- (IBAction)loginButton:(id)sender {
UITabBarController *tbc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyTabBarController"];
[self presentViewController:tbc animated:YES completion:nil];
}
You wouldn't even need this code if you control drag from the "Login" button to the tab bar controller, and choose "Modal". That will create a modal segue which will present the tab bar controller with no code at all.
If you just want to select another tab from the tabBar controller then use something like this:
UITabBarController *tabBar = (UITabBarController *)self.window.rootViewController;
[tabBar setSelectedIndex:3];
Note that if the tabBar controller is the initial view controller you can grab an instance of it it the applicationDidFinishLaunching method and store it in the AppDelegate. Then you'll be able to access it like this:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
Remember to import the AppDelegate.h
I recommand you to use a Singleton shared instance to share multiple informations form multiple controllers.
It's a good design Pattern for you usage.
I'm writing samples of Design Patterns usage on cocoa (see https://github.com/leverdeterre/DesignPatterns -> Real singleton)
Presenting an instance of FBFriendPickerViewController using presentViewController:animated:completion: is pretty straightforward and the class seems like it is meant for that use case. However, I want to push an instance of FBFriendPickerViewController onto an instance of UINavigationController using pushViewController:animated:.
Consider the following code as an example:
self.fbFriendPickerController = [[FBFriendPickerViewController alloc] init];
self.fbFriendPickerController.hidesBottomBarWhenPushed = YES;
// configure stuff
[[self navigationController] pushViewController:self.fbFriendPickerController animated:YES];
However, the problem is that the instance of FBFriendPickerViewController already has a top navigation bar. When pushed onto a UINavigationController, this results in two top navigation bars stacked vertically, as you can see in the screenshot below.
One solution would be to hide the top nav bar of the UINavigationController, but that creates an awkward transition and there is no back button. Any thoughts on the best way to keep the UINavigationController top nav bar but the hide the FBFriendPickerViewController top nav bar?
After looking through the Facebook iOS SDK source code on Github, I figured this out. FBFriendPickerViewController is a subclass of FBViewController. If you set the doneButton and cancelButton properties of any FBViewController to nil, FBViewController will remove the top navigation bar. As a result, the following code works:
self.fbFriendPickerController = [[FBFriendPickerViewController alloc] init];
self.fbFriendPickerController.hidesBottomBarWhenPushed = YES;
self.fbFriendPickerController.doneButton = nil;
self.fbFriendPickerController.cancelButton = nil;
// configure stuff
[[self navigationController] pushViewController:self.fbFriendPickerController animated:YES];
I'm having problems displaying the rightBarButtonItem of the Navigation Bar - I'm attempting to create it programmatically in the Application Delegate, where my UINavigationController is set up.
Code is as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
RSCListViewController *list = [[RSCListViewController alloc] initWithStyle:UITableViewStylePlain];
self.navController = [[UINavigationController alloc] initWithRootViewController:list];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithTitle:#"+"
style:UIBarButtonItemStylePlain
target:list
action:#selector(addPressed:)];
self.navController.navigationItem.rightBarButtonItem = barButton;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
[DatabaseManager openDatabase];
return YES;
}
Running the application, no button item appears on the navigation bar.
I'm not sure whether I have missed something obvious - my attempts to rectify the problem using related Stack Overflow threads haven't yielded any success.
Any help appreciated.
You need to attach your bar button item to your custom view controller, not to the navigation controller. From Updating the Navigation Bar:
In addition, the navigation controller object builds the contents of
the navigation bar dynamically using the navigation items (instances
of the UINavigationItem class) associated with the view controllers on
the navigation stack. To change the contents of the navigation bar,
you must therefore configure the navigation items for your custom view
controllers.
(...)
The navigation controller updates the right side of the navigation bar
as follows:
If the new top-level view controller has a custom right bar button item, that item is displayed. To specify a custom right bar button
item, set the rightBarButtonItem property of the view controller’s
navigation item.
If no custom right bar button item is specified, the navigation bar displays nothing on the right side of the bar.
Therefore, replace:
self.navController.navigationItem.rightBarButtonItem = barButton;
with:
list.navigationItem.rightBarButtonItem = barButton;
I am learning how to manipulate view displays programmatically, I manage to display a new view in my appDelegate with the following block of code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UINavigationController *navController = [[UINavigationController alloc] init];
loginController = [[LoginController alloc] init];
[navController pushViewController:loginController animated:NO];
[self.window addSubview:navController.view];
[self.window makeKeyAndVisible];
return YES; }
I have added a button in this view that will remove this current view and supposedly programmatically display a new view, however, I only managed to remove the view and not display the new view.
my code to display the second view is the following:
HomeController *homeView = [[HomeController alloc] init];
[self.window addSubview:homeView.view];
[homeView.view release];
Please advise.. I've been searching for hours to no avail, using Switch Views Programmatically, iPhone Views, removeSuperview..
Basically I want to create a simple login flow, at app start I will display my first view (login form), after successful login I want to discard the old view and display the second view which is my home page.
You are on the right track with using the UINavigationController. In fact, you are almost there.
You already have two view controllers - one for the login page, and one for the home page. In the didFinishLaunchingWithOptions:, push both controllers onto the UINavigationController's stack: first the "home" controller, then the "login" one. Once the login controller detects that the login has been successful, call popViewControllerAnimated: or popToRootViewControllerAnimated: to get to the home page.
I have a simple app that has two view controllers. Both of them have a UINavigationBar at the top, as a header. The second UIViewController is displayed as a modal view, when the user clicks on a button on the first one.
When my app first launches, the initial view doesn't completely cover the main UIView and seems "pushed" to the top (see image below).
After I click on the "instructions" button, which displays another view with presentModalViewController:animated:, and dismiss the modal ViewController, everything is displayed correctly.
Anybody knows what I might be doing wrong?
I have nothing in viewWillAppear, and this is my viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
if (!self.model) {
self.model = [[FRRSushiRiceModel alloc] init];
[[self.header.items objectAtIndex:0] setTitle: #"Perfect Sushi Rice: Ingredients"];
}
}
and my application:didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create and add the main controller (ingredients)
self.ingredientsController = [[FRRIngredientsViewController alloc] init];
[window addSubview:self.ingredientsController.view];
[window makeKeyAndVisible];
return YES;
}
This small project reproduces this behavior:
Test Case
Did you untick the "Wants Full Screen" setting in IB, either for the UINavigationController or UIViewController?
I found the error, guys.
Basically I was trusting the system to correctly set the frame of my views to match the usable portion of the screen. This works when you add it to some controller of controllers (such as UINavigationController), or add it via IB.
If you add your controllers programmatically, you need to set the view's frame explicitly. A good default is:
[[UIScreen mainScreen] applicationFrame]
represents the part of the screen available to applications: the whole screen minus the status bar.