Laying out views in a TabBarViewController - ios

I'm trying to manually (programmatically) lay out views in a UITabBarViewController. I instantiate my UITabBarViewController like this:
MYAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = [[UITabBarController alloc] initWithNibName:nil bundle:nil];
MYViewController1 *myViewController1 = [[MYViewController1 alloc] init];
myViewController1.title = #"My VC 1";
[tabBarController addChildViewController:myViewController1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = tabBarController;
return YES;
}
MYViewController1.m
- (void)viewDidLoad
{
[super viewDidLoad];
_myView = [[MYView alloc] initWithFrame:self.view.frame];
[self.view addSubview:_myView];
}
MYView.m
- (void)layoutSubviews
{
CGRect descriptionRect, buttonRect;
CGRectDivide(self.frame, &buttonRect, &descriptionRect, 50.f, CGRectMaxYEdge);
_descriptionTextView.frame = descriptionRect;
[self addSubview:_descriptionTextView];
_myButton.frame = buttonRect;
[self addSubview:_myButton];
}
The problem I'm having is that when I get to layoutSubviews, the superview's frame is the full size of the window, so the button is hidden by the tab bar. What am I doing wrong?

Without seeing more code, it appears that you may have some confusion about the use of a UITabBarController.
In the code you have posted above you are initializing a UITabBarController, then a UIViewController, and then you are calling 'addChildViewController:' on the tabBarController. (however, addChildViewController: is an instance method on UIViewController).
Is this in attempt to add the ViewController as a tab of the TabBarController? If so, then try the following code in place of addChildViewController: to see if it gives you the functionality that you are looking for:
tabBarController.viewControllers = #[myViewController1]; // Passing an array of viewControllers will set them as a tabs on the TabBar in the order they are added to the array.
If that doesn't help, then please comment on this answer with more details regarding the desired functionality of your code, and I'll update my answer to assist you as much as I can.
EDIT: Looks like I may have misunderstood your problem. Can you try the following code in play of your subview's initialization:
_myView = [[MYView alloc] initWithFrame:self.view.bounds]; // Try bounds instead of frame.

Related

Adjusting uitableview height

I'm creating a simple application with uitableview. I want to create everything in code. I used following code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
FBVCalendarViewController *calendarViewController = [[FBVCalendarViewController alloc] init];
self.window.rootViewController = calendarViewController;
[self.window makeKeyAndVisible];
return YES;
}
...
- (void)loadView
{
UITableView *calendarItems = [[UITableView alloc] init];
self.view = calendarItems;
}
it works, but application fills the entire phone screen intersecting with standard phone title bar.
What is the right way to adjust view height?
Since UITableView inherits from UIScrollView, you should take care of the changes appeared with IOS 7.
A solution to your problem is:
if ([self respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)]) {
[self setNeedsStatusBarAppearanceUpdate];
self.automaticallyAdjustsScrollViewInsets = NO;
}
(this will keep the table view below the status bar).
Hope that helps. But you should probably have a look at changes introduced with IOS 7.
So I solved my problem with the following code in loadView:
- (void)loadView
{
UITableView *calendarItems = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
UIView *rootView = [[UIView alloc] init];
[rootView addSubview:calendarItems];
self.view = rootView;
}
I used empty UIView as a parent for tableView and changed constructor to explicitly specify UITableView frame. I think that better approach would be to use autolayout (currently it just does not work as expected when I rotate device) and position table view to the full screen or implement device rotation callback and update frame there.

Not understanding how the NavigationController and UIViewControllers are working in iOS

I have a project which I don't really understand the views and navigation behind. I start out in the AppDelegate (MAAppDelegate), where I define properties:
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *detailViewController;
Then in the MAAppDelegate.m, I create a navigationController, and
#implementation MAAppDelegate
#synthesize detailViewController;
#synthesize window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Init the navController for the Master Detail View of the grade cells
UINavigationController *navController = [[UINavigationController alloc] init];
detailViewController = [[UIViewController alloc] init];
UIViewController *viewController = [[MAController alloc] init];
navController = [[UINavigationController alloc] initWithRootViewController:viewController];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
return YES;
}
So at this point, I think I have a working naviationController, I've setup an instance of a custom UIViewController (custom class MAController) and I've set it up as the rootViewController.
Then, in my MAController class, the class where I do all of my UI stuff (the entire UI is done programmatically, no nibs or storyboards). Here is a bit of the viewDidLoad of MAController:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES]; // I commented this line out and realized it does nothing
I go on (in viewDidLoad) to add a bunch of subviews to self.view, like this
[self.view addSubview:self.backgroundImageView];
Earlier, I created a viewController in the AppDelegate class and it was called view, so I assumed it was refereeing to that but now since I've changed it (in AppDelegate) to viewController, I guess I was thinking wrong?
And then finally, I create a UIView in 'viewDidLoad`:
UIView *header = [[UIView alloc] initWithFrame:headerFrame];
header.backgroundColor = [UIColor clearColor];
self.tableView.tableHeaderView = header;
and start adding a bunch of subviews to this new UIView I created header.
So, in short, I have no idea what is happening. Later, when I tried telling (from a method inside MAController) self.navigationController (which I assumed to be navigationController in charge of everything in my project - created at the beginning in the AppDelegate) to pushViewController a new viewController that I was going to use as a detailView for a table, it got weird.
So I'm just trying to understand what has control, and what the rootViewController is, and just what is happening.
The main window root is set to a view controller and not the navigation controller
Change:
self.window.rootViewController = viewController;
to:
self.window.rootViewController = navController;
EDIT:
You can access the navigationController from anywhere by asking your appDelegate. It is normally not considered a good practice:
MAAppDelegate *delegate = (MAAppDelegate *)[[UIApplication sharedApplication] delegate];
UINavigationController *nav = delegate.navigationController;
Don't forget to:
#import "MAAppDelegate.h"
First, take a little time and read through how navigation controllers work. The documentation is really helpful:
https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html
Second, your problem is that your window's root view controller is not the navigation controller you created. Rather it is an instance of MAController. This is what you're doing:
UIViewController *viewController = [[MAController alloc] init];
// some other code ...
self.window.rootViewController = viewController;
I think you meant to add MAController as the root view controller of the navigation controller and make the navigation controller your window's root. If so, you'll want to set your view controllers up like this:
UIViewController *viewController = [[MAController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
self.window.rootViewController = navController;
Another potential problem is that you don't seem to be doing anything with your detailViewController. Maybe that's confusing you too.

Sidemenu with tabBarControllers in Storyboard

Hi Fellow iOS Developers, I am a newbie developing a project with 5 tab Views and on the first and second tabs I have slide out menus using Container views from example code by Michael Frederick on his GitHub page Project Link: https://github.com/mikefrederick/MFSideMenu. He is using a nib (.xib) files though I am using Storyboard to achieve the same and struck with defining the container and child views. can kindly some one advice how to modify the below code to accommodate in my storyboard.
the original code in the AppDelegate.m is
- (DemoViewController *)demoController {
return [[DemoViewController alloc] initWithNibName:#"DemoViewController" bundle:nil];
}
- (UINavigationController *)navigationController {
return [[UINavigationController alloc]
initWithRootViewController:[self demoController]];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:[NSArray arrayWithObjects:[self navigationController],
[self navigationController], nil]];
SideMenuViewController *leftSideMenuController = [[SideMenuViewController alloc] init];
SideMenuViewController *rightSideMenuController = [[SideMenuViewController alloc] init];
MFSideMenuContainerViewController *container = [MFSideMenuContainerViewController
containerWithCenterViewController:tabBarController
leftMenuViewController:leftSideMenuController
rightMenuViewController:rightSideMenuController];
self.window.rootViewController = container;
[self.window makeKeyAndVisible];
return YES;
}
#end
how to modify the code to accommodate the container parent view and child views ?
where should i instantiate the code for the parent and child of the 2nd tab view ? in AppDelegate or the View Controller ?
If any other Details are required leave a comment please. Any Help Will be greatly appreciated. thanks in Advance.
I don't know if you still need this, but i had the exactly same problem today, too. What you need to do is:
remove the both methods over your app Delegate
put this in your app Delegate:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"YOUR_STORYBOARD" bundle:[NSBundle mainBundle]];
MFSideMenuContainerViewController *container = (MFSideMenuContainerViewController *)self.window.rootViewController;
UIViewController *leftSideMenuViewController = [storyboard instantiateViewControllerWithIdentifier:#"THE_IDENTITY_OF_YOUR_SIDEMENU"];
UITabBarController *centerViewController = [storyboard instantiateViewControllerWithIdentifier:#"IDENTITY_OF_YOUR_TABBARCONTROLLER"];
[container setCenterViewController:centerViewController];
[container setLeftMenuViewController:leftSideMenuViewController]; //for the right Side, its the same way...
[container setPanMode:MFSideMenuPanModeNone]; //remove this line, if you need the pan mode
return YES;
In your Storyboard you have to put a ViewController as a subclass from "MFSideMenuContainerViewController". Mark this View as the "Initial View Controller" in the Attribute Inspector. Now use a Segue from your new Initial View Controller and let it "push" to your TabBarController. To avoid a Warning rename the Segue.
After you have done this, you can add a UIBarButtonItem to every View, you like to add the SideMenu. In the Action Method of this UIBarButtomItem you only need to do this:
[self.menuContainerViewController toggleLeftSideMenuCompletion:^{}];
finally make sure you have a UIViewController or a UITableViewController, that is your "SideMenu" and set the right Storyboard ID.
if you are still need help, comment this...
and sorry for my english :)
You can use https://github.com/ozcanakbulut/VoovilSideMenu. It's easy to embed in a tabBarController. It uses Storyboard and Arc.

UITabBarController Without a UITabBar

I'v been trying to hide/remove the UITabBar from a UITabBarController. I've got a UITabBar in my iPhone version but I've moved the navigation to a new view controller for the iPad version. The new menu works using the UITabBarDelegate methods to switch between the UIViewControllers.
So far so good.
All I need now is to somehow hide the UITabBar.
I've got a custom UITabBarController and I've tried simply using self.tabBar.hidden = YES;
but i need the view to fill the screen.
Thank you
So you have a few options. Suppose that your tab views are navigation controllers. In that case you can have a temporary viewController that immediately pushes the "real" viewController you want to use, and the "real" one has the method below implemented. Later, by resetting the navigationControllers viewControllers array, you can get rid of the temporary controller for good.
- (BOOL) hidesBottomBarWhenPushed { return YES; }
If that does not work for you, then you can play games with the window.rootViewController. At launch, you create your viewController and make it the rootViewController. Later, when you want a tab bar, you can message back to the appDelegate to create a tabBarController and make your view the first viewController (as is!). I just verified this in a simple demo app using the Xcode Tab Bar project. Here is the code I used:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
viewController1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
self.window.rootViewController = viewController1;
[self.window makeKeyAndVisible];
return YES;
}
- (void)switcher
{
[viewController1.view removeFromSuperview];
UIViewController *viewController2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil];
self.window.rootViewController = self.tabBarController;
}
Ive Found the answer I was looking for
Hope this is helpful to someone else
for(UIView *view in self.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, self.view.frame.size.height+49, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, self.view.frame.size.height)];
}
}

UIView doesn't become First Responder when parent UIViewController is set as RootViewController

I'm working on Chapter 7 of BNR's iOS Programming book and I've run into a problem. At the start of the chapter I setup a UIViewController (HypnosisViewController) with an UIView (HypnosisView) that responded to motion events in the previous chapter.
I create the UIViewController in the AppDelegate.m file:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
HypnosisViewController *hvc = [[HypnosisViewController alloc] init];
[[self window] setRootViewController:hvc];
...
}
In the HypnosisViewController, I set HypnosisView to become first responder:
- (void)loadView
{
// Create a view
CGRect frame = [[UIScreen mainScreen] bounds];
HypnosisView *view = [[HypnosisView alloc] initWithFrame:frame];
[self setView:view];
[view becomeFirstResponder];
}
And in HypnosisView I make sure to return YES to canBecomeFirstResponder. Unfortunately, the HypnosisView did not respond to motion events like before. When I eventually moved on, I made an interesting discovery. If I move HypnosisViewController into a UITabBarController, HypnosisView starts responding to motion events. The code looks something like this:
HypnosisViewController *hvc = [[HypnosisViewController alloc] init];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
NSArray *viewControllers = [NSArray arrayWithObjects:hvc, <insert more objs here>, nil];
[tabBarController setViewControllers:viewControllers];
[[self window] setRootViewController:tabBarController];
Why didn't HypnosisView become first responder when HypnosisViewController was set as the RootViewController? Why did it start working once HypnosisViewController was placed inside another controller? What am I missing about RootViewController?
Thanks!
Your question is very apt. I'm also studying the same book and am on the same chapter. The thing is that before we used UITabBarController we would either use HypnosisViewController or TimeViewController. And we would then do [self.window setRootViewController:hvc] or [self.window setRootViewController:tvc] in the AppDelegate.m file. In that case setRootViewController method was calling loadView method internally. So if loadView should get called then becomeFirstResponder (which resides inside of it as a method call as per your code) also is supposed to get triggered. So internally canBecomeFirstResponder should get called
Now when we use UITabBarController, things tend to break. What happens is instead of loadView getting called via '[[self window] setRootViewController:tabBarController];' line of code, it gets called through '[tabBarController setViewControllers:viewControllers];'. So the bottomline is that rootViewController property (when set to tabBarController) does not call loadView method and hence 'becomeFirstResponder' is not called. You may argue that loadView does get called through '[tabBarController setViewControllers:viewControllers];' but setViewControllers is not used for setting root viewController.
When I faced this problem, I made an explicit call to becomeFirstResponder. Here's how:-
#implementation HypnoTimeAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions //method of UIApplicationDelegate protocol
{
NSLog(#"lets begin");
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
HypnosisViewController *viewController= [[HypnosisViewController alloc] init];
TimeViewController *viewController2= [[TimeViewController alloc] init];
NSLog(#"view controllers are done initializing!");
UITabBarController *tabBarController= [[UITabBarController alloc] init];
NSArray *viewControllers= [NSArray arrayWithObjects:viewController,viewController2, nil];
[tabBarController setViewControllers:viewControllers];//loadView of HypnosisViewController gets called internally since the 'app view' isn't going to load from a XIB file but from 'HypnosisView.m'.loadView method of TimeViewController loads its own view from the XIB file.
[self.window setRootViewController:tabBarController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
#implementation HypnosisViewController
-(void)loadView{
NSLog(#"HypnosisView loading...");
HypnosisView *myView= [[HypnosisView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.view= myView;
[self configureFirstResponder];//configuring first responder
}
-(void) configureFirstResponder{
BOOL viewDidBecomeFirstResponder= [self.view becomeFirstResponder];
NSLog(#"Is First Responder set as HypnosisView? %i",viewDidBecomeFirstResponder);
}

Resources