Issue with UITabBar, multiple UINavigationController and device orientation - objective-c

I have a UITabBar with 2 UINavigationController created in my AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
UINavigationController *navController1, *navController2;
PolyPage *viewController1 = [[PolyPage alloc] init];
PolyPage *viewController2 = [[PolyPage alloc] init];
navController1 = [[UINavigationController alloc] initWithRootViewController:viewController1];
navController2 = [[UINavigationController alloc] initWithRootViewController:viewController2];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:navController1, navController2, nil];
...
return YES
}
My view controllers are the same. In PolyPage.m I have the method :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:CGPointMake(150, self.tableView.contentOffset.y + 240)];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
imageViewer = [[ImageViewer alloc] initWithDataSource:[NSArray arrayWithObject:cell.imageView.image]];
[self.navigationController presentModalViewController:imageViewer animated:YES];
}
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
This method shows my modal view controller only with the first navigation controller in the UITabBar.
I would like to make it work with both navigation controllers.

Related

iOS 7 Tab bar icons temporarily disappear when on More tab

When I add a view controller embedded by navigation controller, to a tab bar, its icon + title disappear briefly when coming back to the More tab.
However when the view controller is added as such, the icon+image are okay and don't disappear.
I've tried many things already, and am out of options. Any ideas?
Here's my AppDelegate code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.tabBarController = [[UITabBarController alloc] init];
// Must be placed here, just before tabs are added. Otherwise navigation bar
// will overlap with status bar.
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[self addViewControllersToTabBar];
self.window.rootViewController = self.tabBarController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
- (void)addViewControllersToTabBar
{
NSArray* tabBarClassNames =
#[
NSStringFromClass([FirstViewController class]),
NSStringFromClass([SecondViewController class]),
NSStringFromClass([FirstViewController class]),
NSStringFromClass([FirstViewController class]),
NSStringFromClass([FirstViewController class]),
NSStringFromClass([SecondViewController class]),
NSStringFromClass([FirstViewController class]),
];
NSMutableArray* viewControllers = [NSMutableArray array];
for (NSString* className in tabBarClassNames)
{
UIViewController* viewController = [[NSClassFromString(className) alloc] init];
UINavigationController* navigationController;
navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
[viewControllers addObject:navigationController];
}
[viewControllers addObject:[[FirstViewController alloc] init]]; // This one is fine.
self.tabBarController.viewControllers = viewControllers;
self.tabBarController.selectedViewController = viewControllers[2];
}
and the view controllers are literally nothing more than:
#implementation SecondViewController
- (instancetype)init
{
if (self = [super init])
{
self.title = #"second";
self.tabBarItem.image = [UIImage imageNamed:#"second.png"];
}
return self;
}
#end
Holy shit, spent so much time on this bug, but here's my workaround. Instead of:
navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
I created my own NavigationController as subclass of UINavigationController:
#implementation NavigationController
- (instancetype)initWithRootViewController:(UIViewController*)rootViewController
{
if (self = [super initWithRootViewController:rootViewController])
{
NSString* className = NSStringFromClass([rootViewController class]);
NSString* name = [className stringByReplacingOccurrencesOfString:#"ViewController" withString:#""];
self.tabBarItem.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#Tab.png", name]];
}
return self;
}
#end
and then do:
navigationController = [[NavigationController alloc] initWithRootViewController:viewController];
Prerequisite is that tab images have the same base name as the view controller class name, which was already the case in my app.
I was setting self.tabBarItem.image inside the view controllers' init method, and this seems to cause the effect I was seeing. So in addition to using my own navigation controller, I also simply deleted setting the tabBarItem in each individual view controller.

UITabBarController doesn't carry through when i push a new view from a UINavigationController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
FirstViewController *fvc = [[FirstViewController alloc] init];
SecondtViewController *svc = [[FirstViewController alloc] init];
//Create UITabBarController
UITabBarController *theTabBarController = [[UITabBarController alloc] init];
NSArray *viewControllers = [NSSArry arrayWithObjects: fvc, svc, nil];
[theTabBarController setViewControllers:viewControllers];
// Create UINavigationController
UINavigationController *theNavigationController = [[UINavigationController
alloc]initWithRootViewController:theTabBarController];
[[self window] setRootViewController:theNavigationController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Then in the First View Controller i do a push to a second view
- (IBAction)Page2:(id)sender {
SBHomePageDetailViewController *detailPageViewController = [[SBHomePageDetailViewController alloc] init];
// Pushing to the stack
[[self navigationController] pushViewController:detailPageViewController animated:YES];
}
Now my UI shows the second view, however, the UITabBarController is missing. When i navigate back the tab bar view is back. How do I keep the tab bar controller visible in all ui screens?
Into AppDelegate.h file make property of theTabBarController:
#property (nonatomic, strong) UITabBarController *theTabBarController;
And here how I changed your didFinishLaunchingWithOptions method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
FirstViewController *fvc = [[FirstViewController alloc] init];
SecondtViewController *svc = [[SecondtViewController alloc] init];
// Create UINavigationController
UINavigationController *theNavigationController = [[UINavigationController
alloc]initWithRootViewController:fvc];
//Create UITabBarController
self.theTabBarController = [[UITabBarController alloc] init];
NSArray *viewControllers = [NSArray arrayWithObjects: theNavigationController, svc, nil];
[self.theTabBarController setViewControllers:viewControllers];
[[self window] setRootViewController:theNavigationController];
[[self window] addSubview:self.theTabBarController.view];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
The problem in the code is that it tries to initialize a UITabBarController as the rootViewController of a UINavigationController in this line:
// Create UINavigationController
UINavigationController *theNavigationController = [[UINavigationController alloc]initWithRootViewController:theTabBarController];
From the docs:
rootViewController:
The view controller that resides at the bottom of the navigation stack. This object cannot be an instance of the UITabBarController class.
Try removing that line and, per #rmaddy's suggestion, put each View Controller in a Navigation Controller. Then set those Navigation Controllers as the Tab Bar Controller's VCs and set the App's RootViewController to the Tab Bar Controller:
FirstViewController *fvc = [[FirstViewController alloc] init];
SecondtViewController *svc = [[SecondtViewController alloc] init];
// Create the first UINavigationController
UINavigationController *firstNavigationController = [[UINavigationController
alloc]initWithRootViewController:fvc];
// Create the second UINavigationController
UINavigationController *secondNavigationController = [[UINavigationController
alloc]initWithRootViewController:svc];
//Create UITabBarController
theTabBarController = [[UITabBarController alloc] init];
NSArray *viewControllers = [NSArray arrayWithObjects: firstNavigationController, secondNavigationController, nil];
[theTabBarController setViewControllers:viewControllers];
[[self window] setRootViewController: theTabBarController];

UITableVIew not working with UINavigationController

I have encountered a very strange behavior, when I implemented navigationController.
I have a LogInViewController with an UITableView,where cells are populated like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
return cellUsername;
}
if (indexPath.row == 1){
return cellPassword;
}
else{
return nil;
}
}
cellUsername and cellPassword are Cells which I created on .xib..
And when i add this LogInViewController as a rootViewController in my appDelegate it works fine!
I wanted to implement a navigationController, so I created RootViewController, with navController #property and in my appDelagate i called it like this
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
RootViewController *rootView = [[RootViewController alloc]init];
rootView.navController = [[UINavigationController alloc] initWithRootViewController:rootView];
self.window.rootViewController = rootView.navController;
[self.window makeKeyAndVisible];
return YES;
And in my RootViewController on viewDidLoad I have this code:
[super viewDidLoad];
self.navController = [[UINavigationController alloc]init];
LogInViewController *loginView = [[LogInViewController alloc]init];
[[self navigationController] pushViewController:loginView animated:YES];
It opens my LogInViewController,but only the first cell,cellUsername is added to my UiTableView, what is wrong?
Am still a beginner,so I might be missing something stupid probably.. but it works wihout navigationController,so am pretty confused..
Thank you!
I'm not sure if this has anything to do with your problem, but you shouldn't be using this line in your viewDidLoad:
self.navController = [[UINavigationController alloc]init];
Your RootViewController is already embedded in a navigation controller, so you can just use self.navigationController to get a reference to it.
I'm also not sure about this code, it may be ok, but it's not the way it's usually done:
RootViewController *rootView = [[RootViewController alloc]init];
rootView.navController = [[UINavigationController alloc] initWithRootViewController:rootView];
self.window.rootViewController = rootView.navController;
The usual way to do it is like this:
RootViewController *rootView = [[RootViewController alloc]init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:rootView];
self.window.rootViewController = nav;
in AppDelegate file
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
RootViewController *rootView = [[RootViewController alloc]init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:rootView];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
Further there is no need to create another navigationController in the viewDidLoad of the RootViewController class

UITabBarController with a UIPopOverController with Multiple Views

I am working on a small app, according to the requirement the app should have a tabBarItem with 3 items. For this I have programmatically created the tabBarController in the AppDelegate.m file and added the 3 different viewControllers, instantiated them and everything is working good. I see the tabBarItems and all views are working. In one of the views lets say in SecondViewController I show a popOverController where I used a UITableView and populate it with items. When I click one of the items it should show another view lets say sendFeedback. Until there everything is working fine, but as soon as this sendFeedback is presented as the modal view, it occupies the whole app i.e it hides the tabBarItem.
I present the important pieces of code here for review:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
UIViewController *viewController1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
viewController1.title = #"First";
UIViewController *viewController2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
viewController2.title = #"Second";
UITableViewController *tableView3 = [[tableViewController alloc]initWithNibName:#"tableViewController" bundle:nil];
tableView3.title = #"Third";
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, tableView3 ,nil];
self.tabBarController.delegate = self;
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
[viewController1 release];
[viewController2 release];
[tableView3 release];
return YES;
}
In my popOverViewController.m file I am checking which row is selected in the table according to that I present the view
#pragma mark - TableView Delegate Methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
sendFeedback *sendEmailViewController = [[sendFeedback alloc]initWithNibName:#"sendFeedback" bundle:nil];
downLoad *downloadFilelViewController = [[downLoad alloc]initWithNibName:#"downLoad" bundle:nil];
if (indexPath.row == 0)
[self presentModalViewController:sendEmailViewController animated:YES];
else
[self presentModalViewController:downloadFilelViewController animated:YES];
}
Can anyone guide me how to overcome this with the multiple views. In case if anyone requires more information from my side I would be glad to provide.
NOTE: It is the same with the other view (downLoad) as well
EDIT: Here is how I am initializing my PopOverController in the AppDelegate.m file
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if([viewController isKindOfClass:[SecondViewController class]]){
NSInteger index = [[self tabBarController] selectedIndex];
CGRect buttonFrame = [[[[[self tabBarController] tabBar] subviews] objectAtIndex:index+1] frame];
PopOverViewController *popoverView = [PopOverViewController new];
popoverView.contentSizeForViewInPopover = CGSizeMake(250, 85);
popover = [[UIPopoverController alloc]initWithContentViewController:popoverView];
NSLog(#"X:%f Y:%f",buttonFrame.origin.x,buttonFrame.origin.y);
[popover presentPopoverFromRect:buttonFrame inView:self.tabBarController.tabBar permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
}
Thanks
Modal view controllers are used to "block" your application and fulfill a task before you can proceed. So modal view controllers are not what you want to use.
Instead wrap your controllers which have to be shown in the popover in a navigation controller. In the tableView:didSelectRowAtIndexPath: method you can push the corresponding view controller to the navigation stack.
To slove your problem:
At the place where you create the popovercontroller initialize it with a new UINavigationController. And the navigation controller you have to initialize with a rootviewcontroller namely PopOverViewController.m.
PopOverController *popoverContentController = [[PopOverController alloc] init];
UINavigationController *navcon = [[UINavigationController alloc] initWithRootViewController:popoverContentController];
popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverContentController];
And in PopOverController.m
if (indexPath.row == 0)
[self.navigationController pushViewController:sendEmailViewController animated:YES];
else
[self.navigationController pushViewController:downloadFilelViewController animated:YES];

set array .viewControllers from SplitViewController

I'm trying to use a splitview inside a TabBar. By now I have a SplitView in my first TabBarItem. My problem comes when I try to access to a different DetailView or right view in the SplitViewController I have.
I'm trying to do it inside the didSelectRowAtIndexPath: of my Root (or Master) viewcontroller from the SplitView.
Here's the code, where I try to acces to my TabBarController from an AppDelegate object, and change the viewControllers array of my SplitView only changing the second view controller. I always get this crash error, saying that 2nd instance send is unrecognized: -[SecondViewController viewControllers]: unrecognized selector sent to instance 0x6852460
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//object AppDelegate
AppDelegate *myDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//Objecte in Index 0 is my SplitVC
NSArray *barControllers = myDelegate.tabBarController.viewControllers;
if (indexPath.row == 0)
{
SplitVC *temporalSplit = [barControllers objectAtIndex:indexPath.row];
NSArray *mArray = temporalSplit.viewControllers;
FirstViewController *detail = [[FirstViewController alloc]initWithNibName:#"FirstViewController" bundle:nil];
UINavigationController *navigationDetail = [[UINavigationController alloc]initWithRootViewController:detail];
temporalSplit.delegate = detail;
temporalSplit.viewControllers = [[NSArray alloc] initWithObjects:[mArray objectAtIndex:0], navigationDetail, nil];
myDelegate.tabBarController.viewControllers = [NSArray arrayWithObjects:temporalSplit.viewControllers, [barControllers objectAtIndex:1], nil];
}
else if (indexPath.row == 1)
{
SplitVC *temporalSplit = [barControllers objectAtIndex:indexPath.row];
NSArray *mArray = temporalSplit.viewControllers;
Detail2VC *detail = [[Detail2VC alloc]initWithNibName:#"Detail2VC" bundle:nil];
UINavigationController *navigationDetail = [[UINavigationController alloc]initWithRootViewController:detail];
temporalSplit.delegate = detail;
temporalSplit.viewControllers = [[NSArray alloc] initWithObjects:[mArray objectAtIndex:0], navigationDetail, nil];
myDelegate.tabBarController.viewControllers = [NSArray arrayWithObjects:[barControllers objectAtIndex:0], temporalSplit, nil];
}
[myDelegate release];
}
And my AppDelegate code (that works without problems):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
// Override point for customization after application launch.
NSMutableArray *controllersBar = [[NSMutableArray alloc]init];
UIViewController *viewController2 = [[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil] autorelease];
for (int i = 0; i<2; i++)
{
if(i == 0)
{
_firstViewController = [[[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil] autorelease];
_masterApp = [[MasterVC alloc]initWithNibName:#"MasterVC" bundle:nil];
_masterApp.firstViewController = _firstViewController;
_firstViewController.mastervc = _masterApp;
UINavigationController *navigationMaster = [[UINavigationController alloc]initWithRootViewController:_masterApp];
UINavigationController *navigationDetail = [[UINavigationController alloc]initWithRootViewController:_firstViewController];
_splitVC = [[SplitVC alloc] initWithNibName:nil bundle:nil];
//_splitVC.tabBarItem = controller.tabBarItem;
_splitVC.viewControllers = [NSArray arrayWithObjects:navigationMaster, navigationDetail, nil];
_splitVC.delegate = _firstViewController;
[controllersBar addObject:_splitVC];
}
else if (i == 1)
{
[controllersBar addObject:viewController2];
}
}
//self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil];
_tabBarController.viewControllers = controllersBar;
_tabBarController.selectedIndex = 0;
_tabBarController.delegate = self;
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
What am I doing wrong? Any help or suggestion is welcome.
Thanks to all in advance.
Split view controller MUST be root level controllers:
UISplitviewcontroller not as a rootview controller
If not, you can apparently get all sorts of strange things happening.