Strange behaviour of UINavigationController when the applications rootViewController is a tabBarController - ios

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

Related

IOS 7 pushviewcontroller error

This snippet of IOS code used to work prior to IOS 7 to push a new window. It does not anymore
ViewController *secondView = [[ViewController alloc]
initWithNibName:#"ViewController"bundle:nil];
[[self navigationController] pushViewController:secondView animated:YES];
Why doesn't this work anymore?
-------EDIT-----
This is the nav controller in didFinishLaunchingwWthOption
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.launchViewController = [[LaunchViewController alloc]
initWithNibName:#"LaunchViewController" bundle:nil];
self.window.rootViewController = self.LaunchViewController;
[self.window makeKeyAndVisible];
return YES;
-------EDIT----------
The first time I invoke [self addNewView] the view does not appear, with no errors in the console or crashes, I added a button and if on the button press I invoke [self addNewView] the view appears, if I navigate with the back button, I have to tap back twice to return to the initial view. So the view is being created but not shown. Any hints on what could be the problem?
Have you tried to specify tha bundle?
ViewController *secondView = [[ViewController alloc]
initWithNibName:#"ViewController" bundle: [NSBundle mainBundle]];
check this:
UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:self.launchViewController];
self.window.rootViewContro‌​ller = navi;
There is mo navigationController.window.rootViewController should be a navigationController as your logic.

UISplitViewController and CoreData

I am working on a project that uses CoreData one-to-many relationship between folder and files. To show this I am using UISplitViewController, Folders are shown on MasterView and on click of each folder the files are shown on DetailView.Both folders and files are added dynamically.
I have programatically created UISPlitViewController this way
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
FolderViewController = [[FolderViewController alloc] initWithNibName:#"FolderViewController_iPad" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:FolderViewController];
FolderViewController.managedObjectContext = self.managedObjectContext;
fileViewController = [[fileViewController alloc] initWithNibName:#"fileViewController_iPad" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController: fileViewController];
FolderViewController.fileViewController = fileViewController;
self.splitViewController = [[UISplitViewController alloc]init];
self.splitViewController.delegate = fileViewController;
self.splitViewController.viewControllers = #[masterNavigationController, detailNavigationController];
self.window.rootViewController = self.splitViewController;
}
This splits my ipad in to two. Leftside is FolderViewController and rightside is FileViewController.
My master View never hides, in any Orientation.
I have a button on both Master and DetailView which opens common EditViewController modally through splitViewController this way
- (void)Buttonclick
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
testViewController = [[EditViewController alloc] initWithNibName:#"EditViewController" bundle:nil];
m_editViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[appDelegate.splitViewController presentModalViewController:m_editViewController animated:YES];
}
and when I dismiss this View, I add folders or files accordingly.
I dismiss this view this way
[self dismissModalViewControllerAnimated:YES];
I have few doubts here
1) When I launch the app, all imp(main) functions from Both View Controller gets called.is that ok?
2) When I dismiss this ModalView when opened from DetailView, the delegate functions of NSFetchResultsController get called, which are in MasterView . is that ok?
3) As those functions are getting called, my logic fails in some situations.
Regards
Ranjit

Reloading all view controllers in a tab bar controller

I have an iPad app with a UITabBarController with 7 view controllers.
This is in my applicationDidFinishLaunching:
// Override point for customization after application launch.
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.delegate = self;
self.tabBarController.viewControllers = [self createViewControllers];
self.window.rootViewController = self.tabBarController;
And this also in my appDelegate:
- (NSArray *)createViewControllers
{
MyView1 *viewController1 = [[MyView1 alloc] initWithNibName:#"MyView1" bundle:nil];
MyView2 *viewController2 = [[MyView2 alloc] initWithNibName:#"MyView2" bundle:nil];
MyView3 *viewController3 = [[MyView3 alloc] initWithNibName:#"MyView3" bundle:nil];
MyView4 *viewController4 = [[MyView4 alloc] initWithNibName:#"MyView4" bundle:nil];
MyView5 *viewController5 = [[MyView5 alloc] initWithNibName:#"MyView5" bundle:nil];
MyView6 *viewController6 = [[MyView6 alloc] initWithNibName:#"MyView6" bundle:nil];
MyView7 *viewController7 = [[MyView7 alloc] initWithNibName:#"MyView7" bundle:nil];
NSArray *views = [NSArray arrayWithObjects:viewController1, viewController2, viewController3, viewController4, viewController5, viewController6, viewController7, nil];
return views;
}
At any time, I want the user to be able to completely reload those view controllers, so any changes made, any animations in progress, any subviews etc, are flushed from memory and the view controllers are all loaded afresh.
I found the simplest way to do this is to add a shake gesture in the appDelegate. The shake gesture pops up a UIAlertView which asks "Do you want to reset everything?" If yes, this happens:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1) {
self.tabBarController.viewControllers = [self createViewControllers]; // <-- this happens
// some of the other things I've tried
// [self.tabBarController setViewControllers:[self createViewControllers] animated:YES]; // same as above but with animation
// self.tabBarController = [[UITabBarController alloc] init]; // allocations go crazy!
// self.tabBarController.delegate = self;
// self.tabBarController.viewControllers = [self createViewControllers];
// self.window.rootViewController = self.tabBarController;
}
}
I replace the array of view controllers with a freshly created set. You can see a couple of the other things I've tried as well.
What I don't understand is why every time the reset is performed, the allocations in Instruments go up (and stay up) and the app starts to slow down. The more I reset, the more it slows down until it becomes unusable.
I'm using ARC and I would expect it to take care of the memory management each time I refresh the view controllers in the tab bar controller. Yet each refresh eats up more memory, no matter how I do it.
What could it be that is slowing everything down?

Tabbed application won't show Login view

I have a tab bar application in Xcode 4.3 and I'm trying to insert a login screen before the tabbar is shown. The app works OK if presentModalViewController has animated:YESbut if it is without animation the view is not showing.
#synthesize window = _window;
#synthesize tabBarController = _tabBarController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
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;
LogInViewController *logViewController = [[LogInViewController alloc] initWithNibName:#"LogInViewController" bundle:nil];
[self.window addSubview:_tabBarController.view];
[self.tabBarController presentModalViewController:logViewController animated:YES];
//This wont work
//[self.tabBarController presentModalViewController:logViewController animated:NO];
[self.window makeKeyAndVisible];
return YES;
}
-(void)loginDone{
NSLog(#"back to the app delegate");
[self.tabBarController dismissModalViewControllerAnimated:YES];
}
Is this the right way to do it?
Why wont the code work with animated:NO ?
I also get this on output Unbalanced calls to begin/end appearance transitions for <UITabBarController: 0x689d350>.
First of all, move [self.window makeKeyAndVisible]; before your view controller setup.
Additionally, you should be presenting the modal view controller within the viewWillAppear: method of the view controller that will be visible first, to make sure your apps view hierarchy has been fully initialized before presenting your login screen.
Don't do this:
[self.window addSubview:_tabBarController.view];
Do this:
self.window.rootViewController = _tabBarController;
This will put the tabBarController on the screen. But that's not exactly what you want... My advise is:
1) Start by putting the logViewController has the rootViewController as I showed you above.
2) Once you got what you want (login is successful) just tell the AppDelegate to switch the rootViewController. This can be done in with delegation or notifications.
Also, as Toastor indirectly pointed out, you should start the presentViewController from the UIViewController who actually initiates it (and not from the AppDelegate).

Using a navigationcontoller with a tabbar controller

I'm trying to make it so I have a tab bar at the bottom of my screen, and it's always there. I also want it so if I "click" into some menu in one of the tabs, it gives you the option to go back, thus a navigation controller and a tab bar controller.
I still don't really understand iOS, so the answer I found is confusing me.
This Answer: Having a UITabBar AND a UINavigationController in an app?
So how do I implement this? I'm guessing I change this method in my App Delegate,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1, *viewController2;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
viewController1 = [[CFSDFirstViewController alloc] initWithNibName:#"CFSDFirstViewController_iPhone" bundle:nil];
viewController2 = [[CFSDSecondViewController alloc] initWithNibName:#"CFSDSecondViewController_iPhone" bundle:nil];
} else {
viewController1 = [[CFSDFirstViewController alloc] initWithNibName:#"CFSDFirstViewController_iPad" bundle:nil];
viewController2 = [[CFSDSecondViewController alloc] initWithNibName:#"CFSDSecondViewController_iPad" bundle:nil];
}
self.tabBarController = [[UITabBarController alloc] init];
[self.tabBarController setDelegate:self];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
Thanks for your assistance!
First of all you don't need to to manually set up the nib name. You could just name them CFSDFirstViewController~iphone.xib and CFSDFirstViewController~ipad.xib. Then you can call [[CFSDFirstViewController alloc] init] and let iOS do the rest for you. For info see iOS Supports Device-Specific Resources.
About your question, you can only insert the UINavigationController within the UITabBarController. To do it wrap viewController1 within a UINavigationController like the following:
CFSDFirstViewController viewController1 = [[CFSDFirstViewController alloc] init];
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:viewController1];
and then use navController instead of viewController1 like the folliwing
self.tabBarController.viewControllers = [NSArray arrayWithObjects:navController, nil];
Check the code because I wrote by hand. And pay attention to memory if you use a non-ARC project.
Hope it helps.

Resources