I'm currently developing an app using a UINavigationController. I set the root view controller to ViewController1 and then push ViewController2 and then ViewController3 in response to button click events.
If I then click the back button from view 3, I'm returned to view 2 but this view has no back button. Interestingly as well, having set titles for each of these views ('View 1', 'View 2' and 'View 3' respectively), if I navigate from view 3 back to view 2 using the back button, the title changes to 'View 1' i.e. the title for the initial view (view 1) - not the title for view 2.
If anyone has any idea what might be going on here, your suggestions are very much appreciated.
Many thanks in advance!
Edit: I use the following code to init the UINavigationController in the app delegate:
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds]];
self.viewController1 = [[ViewController1 alloc] init];
self.viewController2 = [[ViewController2 alloc] init];
self.viewController3 = [[ViewController3 alloc] init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:viewController1];
self.window.rootViewController = self.navigationController;
I later push view controllers to the UINavigationController on button clicks as follows:
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
[self.navigationController pushViewController:appDelegate.viewController2 animated:YES];
I found the solution - in viewController2 and viewController3 I had the following code in order to hide the navigation bar (I wanted the navigation bar hidden on view1 and then visible on views 2 and 3).
- (void) viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillDisappear:animated];
}
I realised it makes far more sense to do the reverse in viewController1 i.e.
- (void) viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillDisappear:animated];
}
and then removing the previous code from view controllers 2 and 3. This solved the issue.
Related
2 Controllers
PageviewController
1)
- (void)applicationDidFinishLaunching:(UIApplication *)application
[self.window setRootViewController:navigationController];
On bottom toolbar button click in view pushing second UINavigationController:
[self.navigationController pushViewController:cnController animated:YES];
Loading of UIPageViewController in UINNavigationController:
#interface SwipeBetweenViewControllers : UINavigationController <UIPageViewControllerDelegate,UIPageViewControllerDataSource,UIScrollViewDelegate>
On bottom toolbar button click in view:
SampleViewController *viewController = [[SampleViewController alloc] initWithStyle:UITableViewStyleGrouped];
viewController.model = settingsModel;
viewController.navigationItem.title = #"Settings";
[viewController willMoveToParentViewController:self];
// [[self navigationController] setNavigationBarHidden:YES animated:YES];
[self.view addSubview:viewController.view];
[self addChildViewController: viewController];
[viewController didMoveToParentViewController:self];
[self.navigationController pushViewController:viewController animated:YES];
It shows 2 navigation controllers. I need to hide the top one when I issue following:
[[self navigationController] setNavigationBarHidden:YES animated:YES];
It hides UIPageviewcontroller navigation and go back to original view. I also tried:
NSMutableArray *allControllers = [self.navigationController.viewControllers mutableCopy];
[allControllers removeObjectAtIndex:allControllers.count -1];
and also
for (UIViewController *controller in self.navigationController.viewControllers)
{
if ([controller isKindOfClass:[SampleViewController class]])
{
[[self navigationController] setNavigationBarHidden:YES animated:YES];
}
}
How to hide specific original navigation controller?
Thanks for all your help.
I was able to solve this.
Actually, I was pushing viewController to top one #1 navigationcontroller, whereas it was to pushed into bottom one #2 navigationcontroller.
And wrapping #2 navigationcontroller in a containerUIviewcontroller set right navigation.
Thanks
I want my status bar to be drawn over the first section header of UITableView like in the picture. (imagine that pink upper dock is the first section header view)
I am developing an application for iOS 6.0+.
The corresponding UITableViewController is the root VC in my navigation hierarchy. How to achieve this?
Select the view controller in interface builder, and uncheck the box for "Extend Edges: Under Top Bars"
In AppDelegate.h
#property (strong, nonatomic) UINavigationController *navcontrolller;
In AppDelegate.m Call your first class with navigation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; //allocate window view
HomeMainVC *Login_page = [[HomeMainVCalloc] initWithNibName:#"HomeMainVC" bundle:nil];
_navcontrolller = [[UINavigationController alloc] initWithRootViewController:Login_page];
self.window.rootViewController = _navcontrolller;
[self.window makeKeyAndVisible];
}
In HomeMainVC.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBarHidden=YES;
}
-(void)viewWillAppear:(BOOL)animated{
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated{
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillDisappear:animated];
}
In HomeMainVC.m create the table view and other stuff as you want . If you got confused on that too then comment about it.
I have an iPhone app that uses a UINavigationController that is created as so:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create navigation controller and initialize it with the menu view controller.
navigationController = [[UINavigationController alloc] initWithRootViewController:[[MenuViewController alloc] init]];
navigationController.navigationBar.hidden = YES;
navigationController.toolbar.hidden = YES;
// Create main window and initialize it with navigation view controller.
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setRootViewController:navigationController];
[window makeKeyAndVisible];
return YES;
}
From there things usually happen in a sequence similar to the following:
Push SelectDifficultyViewController
Push GameViewController
Push GameOverViewController
Pop to root (MenuViewController)
Instead of popping to the root in step 4, how would I go about switching to a new instance of GameViewController.
I currently have the following but it just returns me to the root:
[self.navigationController popToRootViewControllerAnimated:NO];
[self.navigationController pushViewController:[[GameViewController alloc] initWithStuff:stuff] animated:NO];
As it turns out, the line [self.navigationController popToRootViewControllerAnimated:NO] will result in self.navigationController being nil, which is why the subsequent push to the navigationController does nothing. To fix this, store a local copy of the navigation controller and use that to push after popping to root. Credit for this answer comes from here:
UINavigationController popToRootViewController, and then immediately push a new view
mehinger's answer solved my problem, but I wanted to make it easier to use, so I made the following category.
UINavigationController+PopAndPush.h
#interface UINavigationController(PopAndPush)
- (void)popAndPushToViewController:(UIViewController *)controller animated:(BOOL)animated;
- (void)popAndPushToViewController:(UIViewController *)controller withCustomTransition:(CustomViewAnimationTransition)transition;
#end
UINavigationController+PopAndPush.m
#import "UINavigationController+PopAndPush.h"
#implementation UINavigationController(PopAndPush)
- (void)popAndPushToViewController:(UIViewController *)controller animated:(BOOL)animated {
[self popToRootViewControllerAnimated:NO];
[self pushViewController:controller animated:animated];
}
- (void)popAndPushToViewController:(UIViewController *)controller withCustomTransition:(CustomViewAnimationTransition)transition {
[self popToRootViewControllerAnimated:NO];
[self pushViewController:controller withCustomTransition:transition subtype:nil];
}
#end
On View1 I hide the navigationBar in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES];
}
Then I navigate to View2 where I show the navigationBar
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:NO];
self.title = #"Title";
}
But on back to View1 again, the navigationBar doesn't hide, even if I did tried to hide it after the pushViewController in View2
[self.navigationController pushViewController:View1 animated:YES];
[self.navigationController setNavigationBarHidden:YES];
I also tried to hide the navigation from viewWillAppear in View1 and it hides it, but there is an ugly delay and I don't find it as a good practice.
So can anyone help me with this issue, how can I hide correctly the navigationBar on back to View1?
The best practice to do what you want is putting bellow in your first viewController:
- (void)viewWillAppear:(BOOL)animated{
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated{
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillDisappear:animated];
}
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.navigationController setNavigationBarHidden:YES];
}
The ViewController1 is not going to get allocated again and so viewDidLoad is not going to get called.
You can do it in viewWillAppear though. But if you are saying that there is a delay, you can do one more thing.
You can get the reference ofViewController1 in ViewController2. Suppose ViewController1 is the first controller in the navigation controller, then do this:
//ViewController2.m
- (IBAction)backButtonPressed:(id)sender{
ViewController1 *view1 = [self.navigationController.viewControllers objectAtIndex:0];
[view1.navigationController setNavigationBarHidden:YES];
Your code is correct, but you need to write like this:
[self.navigationController setNavigationBarHidden:YES];
first, then write
[self.navigationController pushViewController:View1 animated:YES];
See when you are pushing View2 from View2 in navigation stack than View1 doesn't gets deallocated. it is there in in the stack. So when you popping out View2 that time View1 viewDidLoad won't get called. so your code setNavigationBarHidden to hide navigation bar doesn't executes. So put that code to ViewWillAppear or ViewDidAppear because these methods gets called every time View appears.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES];
}
I am in kind of situation that I need to start with a tab based application and in that I need a split view for one or more tabs. But it seems that split view controller object can not be added to the tabbarController. (Although tabbar object can be added to the splitviewcontroller).
The problem can be seen otherways: I have a full screen in the left part I have a table view when any row is selected in the table a popover should come out pointing that row. Now when any row in the popover is selected the rows in this popover comes to the left under the selected row (only this row would be visible) and another popover comes out from the selected row. (Breadcrumb navigation type)
I think I am clear in what I explained. So guys any ideas or work arounds?
Please let me know if I am not clear in my question.
Thanks,
Madhup
Using the interface builder, create a split view controller and a tab bar controller and link them to your outlets:
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) IBOutlet UISplitViewController *splitViewController;
In your app delegate didFinishLaunchingWithOption, assign your split view controller to the tab bar controller:
splitViewController.tabBarItem = [[[UITabBarItem alloc] initWithTitle:#"Title" image:nil tag:0] autorelease];
NSArray *controllers = [NSArray arrayWithObjects:splitViewController, /* other controllers go here */ nil];
tabBarController.viewControllers = controllers;
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
This will create a tab bar controller (with only 1 tab in this case), which is displayed correctly in all orientations.
I've written up a subclass for the UISplitViewController that will listen for changes to device orientation and orient itself accordingly. With this class, I can now place split views within a UITabBarController and each split view will behave correctly upon rotation, even if it's not the frontmost tab. I've successfully deployed this in TexLege and it was approved for use in the App Store, but your mileage may vary. Please see the repository at Github.
Feel free to fork and modify it, and I'm always interested in hearing comments (or complaints) about it. https://github.com/grgcombs/IntelligentSplitViewController
I made a sample application. and found we can do it programmatically like:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSMutableArray *array = [NSMutableArray array];
NSMutableArray *tabArray = [NSMutableArray array];
UISplitViewController *splitViewConntroller = [[UISplitViewController alloc] init];
MainViewController *viewCont = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
viewCont = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
[splitViewConntroller setViewControllers:array];
[tabArray addObject:splitViewConntroller];
[splitViewConntroller release];
array = [NSMutableArray array];
splitViewConntroller = [[UISplitViewController alloc] init];
viewCont = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
viewCont = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
[splitViewConntroller setViewControllers:array];
[tabArray addObject:splitViewConntroller];
[splitViewConntroller release];
// Add the tab bar controller's current view as a subview of the window
[tabBarController setViewControllers:tabArray];
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
Hope this helps.
To let a tabbarcontroller appear as a master view for splitviewcontroller you should rewrite tabbarcontroller so that it will support or orientations(so say, using a category for the class UITabBarController)
See my post about retrofitting split view controllers to an existing tab bar interface: http://markivsblog.blogspot.com/2010/04/retrofitting-ipad-uisplitviewcontroller.html
I created a UITabBarController subclass which properly propagates the rotation messages to all UISplitViewControllers it contains. This maintains the correct internal state of the UISplitViewControllers. However, one of the SplitViewController delegate methods is not called if the SplitViewController is not visible, so I account for this in the detail view controller viewWillAppear method. I've confirmed this works in iOS5.0 - iOS6.1
OSTabBarController.m
#import "OSTabBarController.h"
#implementation OSTabBarController
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
for(UIViewController *targetController in self.viewControllers){
if(targetController != self.selectedViewController && [targetController isKindOfClass:[UISplitViewController class]]){
[targetController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
}
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
for(UIViewController *targetController in self.viewControllers){
if(targetController != self.selectedViewController && [targetController isKindOfClass:[UISplitViewController class]]){
[targetController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
}
}
#end
DetailViewController
#implementation OSDetailViewController
-(void)viewWillAppear:(BOOL)animated{
//the splitViewController:willHideViewController:withBarButtonItem:forPopoverController: may not have been called
if(!UIInterfaceOrientationIsPortrait(self.interfaceOrientation)){
self.navigationItem.leftBarButtonItem = nil;
}
}
#pragma mark - UISplitViewControllerDelegate Methods
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
}
#end
Keep in mind that OS 3.2 does not provide proper support for a splitview as a tabbar view.
You can make it "work" but it will have bugs - the biggest is that an orientation change made on another tab's view will often not propagate to the splitview tab view properly, making the view go wacky when you go back to it (left side view takes over the screen, or the barbutton item is missing, etc.).
I've reached the conclusion that I have to create my own splitview for use in a tabBarController because of this issue.
I had heard rumors that Apple was working on a fix but it's been months now and no iPad OS updates have occurred - maybe OS 4 for the iPad will address it.
You can use IB to build tabtab and modify tabs to splitviewcontroller.
-(void) makeSplitViewController {
NSMutableArray *controllers = [NSMutableArray arrayWithArray:tabBarController.viewControllers];
int index = 0;
for (UIViewController *controller in tabBarController.viewControllers) {
if ([controller.tabBarItem.title isEqualToString:#"Stock"]) {
stockDetailController = [[StockDetailController alloc] initWithNibName:#"StockDetailController" bundle:nil];
stockMasterController = [[StockMasterController alloc] initWithStyle:UITableViewStylePlain];
stockMasterController.navigationItem.title = date;
stockMasterController.stockDetailController = stockDetailController;
UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:stockMasterController] autorelease];
splitViewController = [[UISplitViewController alloc] init];
splitViewController.tabBarItem = controller.tabBarItem;
splitViewController.viewControllers = [NSArray arrayWithObjects:nav, stockDetailController, nil];
splitViewController.delegate = stockDetailController;
[controllers replaceObjectAtIndex:index withObject:splitViewController];
}
index++;
}
tabBarController.viewControllers = controllers;
}
We succeeded in having a UISplitViewController inside a UITabViewController on iPad with iOS5+.
to make a long story short: it works:
out of the box if you accept a split also in portrait;
with a bit of
work, if you want to have the master view hidden in portrait, and
have it appear only upon tapping a button.
The trick in the second case is to use the IntelligentSplitViewController (see a few posts up, thanx Greg Combs) or similarly extend a UISplitVC, and be careful that the delegate of the subclass of the splitview controller is always a live object.
We have detailed the process on:
https://devforums.apple.com/message/763572#763572