I have three view controllers: A -> B -> C managed by a navigation controller. A is a transient view controller. It asks the server for something. If the server says everyhing is OK, then A pushes B onto the stack. B must hide the back button because I don't want users to manually go back to A.
// B view controller
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
self.title = #"B";
}
B then pushes C onto the stack when the user taps a table cell.
// B view controller
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
C *c = [[C alloc]
initWithStyle:UITableViewStyleGrouped
];
[self.navigationController
pushViewController:c
animated:YES
];
[c release];
}
.
// C view controller
- (void) viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = NO;
self.title = #"C";
}
If all goes well, the flow should look like this:
------------- ------------- -------------
|_____A_____| |_____B ____| | <B|__ C___|
| | => | | => | |
| loading...| | cells | | detail |
| | | | | |
------------- ------------- -----------
For some reason, C does not show a back button to go back to B until I rotate the device. Once rotated, the back button appears in all orientations. The problem seems to stem from B hiding the back button and C trying to reveal it again, because If I don't let B hide it, I don't have this problem. So how do I get C to show the back button without forcing the user to rotate the device like a monkey?
Update
Broken on two different Verizon iPhone 4 both on iOS 4.2.10
Fine on AT&T iPhone 3GS on iOS 5.0
Fine on AT&T iPhone 4 on iOS 4.3
After some searching I found this solution for iPhone 4.2 (since you posted that it works on later versions) on some old forum post.
-(void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.navigationItem.hidesBackButton = NO;
}
Mayhaps this will help you out.
(Check this out: Back button don't appear in navigationController)
I think you need to put your code like this for C
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.navigationItem.hidesBackButton = NO;
}
I had this problem once and solved it by giving B's navigation item a title
// B view controller
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
self.navigationItem.title = #"What you want C's back button to say";
self.title = #"B";
}
If you don't want the title to appear in B you can set B's navigationItem's titleView to an empty view. About the titleView property:
If this property value is nil, the navigation item’s title is displayed in the center of the navigation bar when the receiver is the top item. If you set this property to a custom title, it is displayed instead of the title. This property is ignored if leftBarButtonItem is not nil.
Try adding this to your class C:
-(id) init
{
[super init];
self.navigationItem.hidesBackButton = NO;
}
Not sure if this will work for your back button situation but I know that when I use a custom back button I need to set the custom back button on the alloc'd item before I push it (not self like the post above). Hopefully it will work for your situation as well - worth a try.
In other words, try:
// B view controller
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
C *c = [[C alloc]
initWithStyle:UITableViewStyleGrouped];
// *** set on c's navigationItem (not self) before you push
[[c navigationItem] setHidesBackButton: NO];
[self.navigationController
pushViewController:c
animated:YES
];
[c release];
}
Try to put self.navigationItem.hidesBackButton = NO; into the init method or anywhere before the pushViewController call.
ViewDidLoad is called when you ask the controller for the view for the first time, that means, it is probably called from [self.navigationController pushViewController:c animated:YES]. But note that the navigation bar is not part of your view, it is created and handled by UINavigationController, so basically it can exist and be drawn even before viewDidLoad and viewDidAppear is called. If you update navigation bar there, it doesn't get actually repainted.
Edit 1: Revisited after reading the documentation for [UIViewController navigationItem]
You should avoid tying the creation of bar button items in your navigation item to the creation of your view controller’s view. The navigation item of a view controller may be retrieved independently of the view controller’s view. For example, when pushing two view controllers onto a navigation stack, the topmost view controller becomes visible, but the other view controller’s navigation item may be retrieved in order to present its back button. To ensure the navigation item is configured, you can override this property and add code to load the bar button items there or load the items in your view controller’s initialization code.
Edit 2: Revisited after reading the comment that my solution does not work. Working code (iOS 5, ARC):
//
// TestAppDelegate.m
// NavigationTest
//
// Created by Sulthan on 10/25/11.
// Copyright (c) 2011 StackOverflow. All rights reserved.
//
#import "TestAppDelegate.h"
#interface TestAppDelegate ()
#property (nonatomic, strong, readwrite) UINavigationController* navigationScreen;
#property (nonatomic, strong, readwrite) UIViewController* screen1;
#property (nonatomic, strong, readwrite) UIViewController* screen2;
#property (nonatomic, strong, readwrite) UIViewController* screen3;
#end
#implementation TestAppDelegate
#synthesize window = window_;
#synthesize navigationScreen = navigationScreen_;
#synthesize screen1 = screen1_;
#synthesize screen2 = screen2_;
#synthesize screen3 = screen3_;
- (UIViewController*)createTestScreenWithLabel:(NSString*)label {
CGRect bounds = [[UIScreen mainScreen] bounds];
UIViewController* screen = [[UIViewController alloc] init];
screen.view = [[UILabel alloc] initWithFrame:bounds];
screen.view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
((UILabel*) screen.view).textAlignment = UITextAlignmentCenter;
((UILabel*) screen.view).text = label;
return screen;
}
- (void)pushThirdScreen {
if (!self.screen3) {
self.screen3 = [self createTestScreenWithLabel:#"Screen 3"];
self.screen3.navigationItem.hidesBackButton = NO;
}
[self.navigationScreen pushViewController:self.screen3 animated:YES];
}
- (void)pushSecondScreen {
self.screen2 = [self createTestScreenWithLabel:#"Screen 2"];
self.screen2.navigationItem.hidesBackButton = YES;
UIBarButtonItem* button = [[UIBarButtonItem alloc] initWithTitle:#"Go"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(pushThirdScreen)];
self.screen2.navigationItem.rightBarButtonItem = button;
[self.navigationScreen pushViewController:self.screen2 animated:YES];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
CGRect bounds = [[UIScreen mainScreen] bounds];
self.screen1 = [self createTestScreenWithLabel:#"Screen 1"];
self.navigationScreen = [[UINavigationController alloc] initWithRootViewController:self.screen1];
self.window = [[UIWindow alloc] initWithFrame:bounds];
self.window.backgroundColor = [UIColor whiteColor];
[self.window addSubview:self.navigationScreen.view];
[self.window makeKeyAndVisible];
[self performSelector:#selector(pushSecondScreen) withObject:nil afterDelay:3.0];
return YES;
}
#end
Edit 3: Revisited after noticing that you are speaking mainly about iOS 4.2. I can't currently test it on any iOS 4.2 but I know about a possible workaround. You can always hide the navigation bar in your UINavigationController and just put a separate navigation bar into each screen. You will have absolute control over them and you can even edit them in Interface Builder.
Related
I am developing an application which has a a TableView. When I press any cell the app goes to the next ViewController. In this viewController I have created a TabBarController by code which has 3 children ViewControllers. So, I want to pass a variable from the TableView to the Children of the TabBar. I can pass the variable to the TabBar, I have watched it with the NSlog function. It is really weird for me that in the children ViewControllers I also have type a NSlog and the variable is null, but in the output I see first this.
2013-10-01 03:01:40.687 Prototype[38131:c07] proId (null) // This is the children log from vc2 ViewController "YPProjectViewController"
2013-10-01 03:01:40.697 Prototype[38131:c07] projectID 433 // This is the TabBar LOG YPTabBarViewController
Does somebody know why I can first the Children NSLog? Maybe there is the solution.
#import "YPTabBarViewController.h"
#import "YPProjectViewController.h"
#import "YPCommentsViewController.h"
#import "YPProposalsViewController.h"
#interface YPTabBarViewController ()
#property (nonatomic,strong)UITabBarController *tabBar;
#end
#implementation YPTabBarViewController
#synthesize tabBar;
#synthesize projectId = _projectId;
- (void)viewDidLoad
{
[super viewDidLoad];
[self setUpTabBar];
}
// Set up tabBar
-(void)setUpTabBar
{
YPCommentsViewController *vc1 = [[YPCommentsViewController alloc] init];
vc1.title = #"Comments";
vc1.view.backgroundColor = [UIColor clearColor];
UINavigationController *contentNavigationController = [[UINavigationController alloc] initWithRootViewController:vc1];
YPProjectViewController *vc2 = [[YPProjectViewController alloc] init];
vc2.title = #"Project";
vc2.view.backgroundColor = [UIColor clearColor];
vc2.proId = _projectId;
NSLog(#"PROJECT ID %#", vc2.proId);
// UINavigationController *contentNavigationController2 = [[UINavigationController alloc] initWithRootViewController:vc2];
YPProposalsViewController *vc3 = [[YPProposalsViewController alloc] init];
vc3.title = #"Proposal";
vc3.view.backgroundColor = [UIColor clearColor];
UINavigationController *contentNavigationController3 = [[UINavigationController alloc] initWithRootViewController:vc3];
tabBar = [[UITabBarController alloc] init];
tabBar.viewControllers = #[contentNavigationController,vc2,contentNavigationController3];
tabBar.selectedIndex = 1;
[tabBar.view setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[tabBar willMoveToParentViewController:self];
[self addChildViewController:tabBar];
[tabBar didMoveToParentViewController:self];
[self.view addSubview:tabBar.view];
}
In terms of understanding the problem, your NSLog statement in the "tab bar controller" is logging the value of vc2.proID immediately after setting it. But your NSLog output show us that that second tab's view controller is logging its results before that. That's why it's nil when the second tab's view controller's viewDidLoad logs it, because that log is happening before the tab bar controller had a chance to set the value and log it itself.
So, there are a couple of ways you could fix this:
Right before your assignment of vc2.proId, you have an innocuous line of code that says:
vc2.view.backgroundColor = [UIColor clearColor];
That line of code triggers the second view controller's view to be loaded (and its viewDidLoad will be called). If you move the assignment of vc2.proId to before you start accessing any of vc2's views, that will change the order that your NSLog statements appear (or, much better, move the setting of the background color into viewDidLoad of the child controllers).
You could just create your own init method that accepts the project id as a parameter. That would also ensure that it's set before viewDidLoad. Thus, YPProjectViewController could have a method such as:
- (id)initWithProjectId:(NSString *)projectId
{
self = [self init];
if (self)
{
_proId = projectId;
}
return self;
}
Two unrelated observations regarding the custom container calls:
When you call addChildViewController, it calls willMoveToParentViewController for you. So you should remove the call to willMoveToParentViewController. See the documentation for that method.
You might even want to retire these custom container calls altogether, and just make YPTabBarViewController a subclass of UITabBarController, itself, rather than UIViewController. That eliminates the need to custom container calls altogether. Clearly, if you have other needs for the custom container, then feel free, but it's redundant in this code sample.
I am trying to show a UIImagePickerController from a button click. When I click the button, I get a SIGABRT at the line:
[self presentModalViewController:camera animated:YES];
from the code block:
camera = [[UIImagePickerController alloc]init];
[camera setSourceType:UIImagePickerControllerSourceTypeCamera];
[camera setDelegate:self.view];
camera.showsCameraControls = NO;
camera.navigationBarHidden = YES;
camera.wantsFullScreenLayout = YES;
camera.toolbarHidden = YES;
camera.cameraOverlayView = bottomArrow;
[self presentModalViewController:camera animated:YES];
where camera is the name of the UIImagePickerController defined as such:
UIImagePickerController *camera;
in the #interface.
My interface declaration is:
#interface cameraViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate> {
Can someone see what I'm doing wrong?
Besides the good point made by #Vikings, always check if your device has a camera before trying to use it:
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[camera setSourceType:UIImagePickerControllerSourceTypeCamera];
} else {
[camera setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
}
Make sure you are using the both the Navigation Controller Delegate and the Image Picker Controller Delegate. The Image Picker is actually a Navigation Controller, which is why you have to implement its delegate.
#interface YourViewController : UITableViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
Also, set the delegate correctly, not to the view, but the View Controller.
camera.delegate = self;
The delegate needs to be set to the View Controller, and not the View Controller's View.
Check out the code below:
(1) You do not need to hide the navigation bar, because there is not one
(2) You do not need to hide the toolbar, because there is not one
(3) You do not need to specify wantsFullScreenLayout, because a Modal View Controller will always take up the full screen
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.showsCameraControls = NO;
// Comment out the line below to make sure it is not causing a problem.
// This just expects a view, so if bottomArrow is a view you should be fine
picker.cameraOverlayView = bottomArrow;
[self presentModalViewController:picker animated:YES];
Also, I did not realize you were loading this code in viewDidLoad, this will crash, because the View Controller itself is not finished it's transition, so you cannot begin another transition. Instead use viewDidAppear for the same effect:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
// Place code here
}
Do not put your code in viewDidLoad create one IBAction and put your code inside that function.
and set delegate to your view controller. and in .h file make sure that your wrote
<UIImagePickerControllerDelegate>
[camera setDelegate : self];
I have page-based app. On each page I have 3 uibuttons at the top, uiscrollview with alphabet (uibuttons to sort data in uitable) at the right and uitableview at the center. How to show cell's detail view? If it is necessary to add uinavigationcontroller I can't do this. If I adds it, it disables interaction with my table, buttons and scrollview.
And another question is how to show new data in tableview and scrollview when goes to next page??
I have rootViewController class and DataViewController class.
rootViewController listing:
#interface RootViewController ()
#property (readonly, strong, nonatomic) ModelController *modelController;
#end
#implementation RootViewController
#synthesize pageViewController = _pageViewController;
#synthesize modelController = _modelController;
#synthesize navContr = _navContr;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Configure the page view controller and add it as a child view controller.
//[self presentModalViewController:navContr animated:YES];
self.pageViewController = [[[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil] autorelease];
self.pageViewController.delegate = self;
DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = [NSArray arrayWithObject:startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
self.navContr = [[UINavigationController alloc] initWithRootViewController:self.pageViewController];
[self.view addSubview:self.navContr.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
for (UIGestureRecognizer *recognizer in self.pageViewController.gestureRecognizers){
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]){
[recognizer setEnabled:NO];
}
}
}
After few manipulations it works but I need help to get it work fine!
So now it looks like this
Next question: how do I remove the brown space at the top???
::UPDATE::
Problem solved. It needs just to set y-axis position of UINavigationController to -20 ;)
i'm not sure if this link about creating navigation-based project may help you.. (http://iosmadesimple.blogspot.com/2012/08/navigation-based-project-doing-it.html)
From that tutorial, there's a class called SampleViewController, subclass of UIViewController. You might want to put a tableView in SampleViewController.xib file. Then in your SampleViewController.h file, add an IBOutlet UITableView* yourTable property and synthesize it. Connect it to your tableView in your .xib file. //Or you may do it programmatically
in your SampleViewController.h, make your interface header look like this.. I think you already know this...
#interface SampleViewController:UIViewController < UITableviewDelegate, UITableViewDatasource >
in your SampleViewcontroller.m, under viewDidLoad method, set the table delegate and datasource to self:
yourTableView.delegate = self;
yourTableView.datasource = self;
Afterwhich, you implement tableView delegate and datasource methods... //you already know those since you were already able to show a tableview ;)
one of these methods is the "tableview:didSelectAtIndexpath:" --> this is the part wherein you can put your code when you click one of the cells.
Let's assume you have the DetailsViewController Class, this is the class you would like to show after clicking a cell and show its details.
DetailsViewController Class must have a variable that will accept the data you would like to show. Let's say, an NSString *detailsMessage; //do the #property and #synthesize thing...
Let's go back to SampleViewController.m file, under tableview:didSelectAtIndexpath: Method:
inside that Method.. put these codes.
DetailsViewController *detailsVC = [[DetailsViewController alloc] init];
detailsVC.detailsMessage = #"The Data you want to pass.";
[self.navigationController pushViewController:detailsVC animated:YES];
I hope this helps. :(
There may be other ways but by far the easiest way is to use a navigation controller. In fact, it is built to do exactly this.
If you don't want the navigationBar then you can hide it in the viewWillAppear function.
[self.navigationController setNavigationBarHidden:YES animated:YES];
Then you can add an other UIViewController to push when the user selects a cell.
Having read your OP again I'm not sure how you are adding your navigationController.
To use a navigationController you create it and load it at start time. You then create your current viewController (the one with the buttons and table etc...) and set this as the rootViewController of the navigationController.
Then you display the navigationController.
Could you explain how you are adding your navigationController as it may help understand what is going wrong.
Thanks
::EDIT::
OK, my assumption was correct.
The way you are using the navigation controller is not how it was intended.
OK, so at the moment your AppDelegate file will have a method Application didFinishLaunching...
It will look something like this...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[OJFViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
You should change it so that it is like this...
first add a property to your appDelegate...
#property (nonatomic, strong) UINavigationController *navigationController;
Then change the didFinishLaunchingMethod to this...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[OJFViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
This will still show you MainViewController but it will now be contained within a navigationController.
Next in your MainViewController function viewWillAppearAnimated add the line...
[self.navigationController setNavigationBarHidden:YES animated:animated];
This will then hide the navigationBar at the top of the view so you still have access to your buttons.
The you need a new ViewController and xib file (for example DetailViewController).
When the user selects a table row you need to then do something like...
DetailViewController *detailView = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
//pass in details of which row was selected.
[self.navigationController pushViewController:detailView animated:YES];
This will then display your new view and new viewController. You will also need to write a way of passing the data in (set up a property on the DetailViewController).
Hope this helps.
I have a requirement to load a UIWebView wrapped inside a UINavigationController, and show it on a modal view. UINavigationController should show the navigation bar at the top with a 'back' (I did not find back button, so I used 'done' button). I am not supposed to use a nib, so I have to do it programmatically only. The code has to basically serve as a library which can be integrated with any application. It has to work for IPhone and IPad, both.
This is what I have done so far:
I created a WebViewController class -
#interface WebViewController : UIViewController
{
UIWebView *m_cWebView;
}
#property (nonatomic, retain) UIWebView *m_cWebView;
#end
- (void)loadView
{
CGRect webFrame = [[UIScreen mainScreen] applicationFrame];
webFrame.size.height -= self.navigationController.navigationBar.frame.size.height;
UIWebView *pWebView = [[UIWebView alloc] initWithFrame:webFrame];
pWebView.autoresizesSubviews = YES;
pWebView.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
self.view = pWebView;
pWebView.scalesPageToFit = YES;
self.m_cWebView = pWebView;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(back:)];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if( m_cWebView != nil )
{
NSURL *url = [NSURL URLWithString:#"http://www.google.co.in"];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[m_cWebView loadRequest:request];
}
}
- (IBAction)back:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
return nil;
}
- (id)init
{
return self;
}
In the main view controller
#property (retain, atomic) UINavigationController *navCon;
-(IBAction)buttonPressed:(id)sender
{
if( navCon == nil )
{
WebViewController* webViewController = [[WebViewController alloc] init];
navCon = [[UINavigationController alloc] initWithRootViewController:webViewController];
}
[self presentModalViewController:navCon animated:YES];
}
So far, it is working fine. Now my issues:
I am totally new to iOS world. Is the above code correct or are there issues?
Code is supposed to be compiled with Xcode 4.2 with ARC, so I presume that I don't need to worry about memory.
Is the logic to calculate the initial size of WebView correct (I am taking the size of main screen and deducting the height of navigation bar)?
How do I handle orientation change such that on changing the orientation, my Navigation View and WebView both adjust to the new orientation?
Thanks
Edit:
I tried implementing with the rotation, but I have not been able to get it to work. willRotateToInterfaceOrientation is not getting called on my WebViewController, even though I am returning YES from shouldAutorotateToInterfaceOrientation ( and this method is getting called ). When I tried working with an empty nib ( loading WebViewController with this empty nib ), then it works fine.
Adding the 5th requirement:
One more additional requirement is that when this modal dialog dismisses, the view which presented this, should be in correct orientation (i.e. while the modal dialog is up, the orientation events are also passed on to the view which presented this modal view).
Thanks
Some points from my side.
Regarding back button, in place of done:
self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:nil action:nil] autorelease];
Height of navigation bar is always 44.0f
You can handle orientation change with these methods:
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self adjustViewsForOrientation:toInterfaceOrientation]; // adjust your view frames here
}
I am not sure about your code and memory issues in that - so i could not comment over those.
Hope this information would be helpful to you.
I found the fix to the rotation problem.
Replace
- (id)init
{
return self;
}
with
- (id)init
{
if (self = [super init])
return self;
return nil;
}
and completely remove the following method:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
return nil;
}
Silly mistakes :)
Now it is rotating fine.
Can someone still comments on the original requirements?
I have a UINavigationController that contains 3 UIViewControllers on the stack.
View A - is the root
View B - is pushed by View A and has `self.navigationItem.hidesBackButton = YES;`
View C - is pushed by View B and has `self.navigationItem.hidesBackButton = NO;`
View C does not show the back button, even though I have hidesBackButton set to NO. How can I resolve this?
Update
A possible bug in 4.2 as it works till 4.1 sdks
I have tried this and mine is working perfectly. I am just posting the implementation of B view controller (BVC) and C view controller (CVC). My initial guess is that you are not setting the title of BVC in viewDidLoad.
#implementation BVC
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"I am B";
}
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = YES;
}
- (IBAction)pushB:(UIButton *)sender{
CVC *cvc = [[CVC alloc] initWithNibName:#"CVC" bundle:nil];
[self.navigationController pushViewController:cvc animated:YES];
[cvc release];
}
#end
#implementation CVC
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = NO;
}
#end
I think you have to set that property before you push or pop a view controller to affect the next view controller, setting it for the current viewcontroller in viewWillAppear is too late.
Edit: this looks like a bug in 4.2! The back button remains hidden both in the 4.2 simulator and on the device with 4.2, but it works in the 3.2, 4.1, and 4.0 simulators!
Here's the code where when pushing a VC with a hidden back button:
- (IBAction) goto2nd
{
SecondVC *vc = [[[SecondVC alloc] initWithNibName:#"SecondVC" bundle:nil] autorelease];
vc.navigationItem.hidesBackButton = YES;
[self.navigationController pushViewController:vc animated:YES];
}
That is all that should be needed, each VC has its own navigationItem, it's not a global setting, so you don't need to bother undoing it to restore the back button (at least when popping back to a VC where it is set to "NO").
Here's a workaround that I'm using successfully on 4.3.
Instead of hiding the back button, set the left bar button view to an empty view:
UIView *tmpView = [[UIView alloc] initWithFrame:CGRectZero];
UIBarButtonItem *tmpButtonItem = [[UIBarButtonItem alloc] initWithCustomView:tmpView];
[tmpView release];
self.navigationItem.leftBarButtonItem = tmpButtonItem;
[tmpButtonItem release];
To restore the back button, just set the left bar button item to nil:
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
Update: It appears as if the bug is gone in 4.3.
Note: Even though the bug seems to be fixed, I prefer the "empty view" technique because it allows the disappearance and reappearance of the back button to be animated.
The solution for this problem is somewhat tricky..just try it it will surely work since even I faced the same problem.
First set Navigation title in viewWillAppear.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationItem.title = #"SET YOUR TITLE";
}
When you are navigating to other page just set your navigation title to null.This will not show you any button on top.Since you can get rid of writing
self.navigationItem.hidesBackButton = YES; everytime.
- (IBAction)pushB:(UIButton *)sender
{
SecondVC *vc = [[[SecondVC alloc] initWithNibName:#"SecondVC" bundle:nil] autorelease];
self.navigationItem.title = #"";
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
I'm running the same issue and it's only happening on the iOS 4.2 simulator, so probably it's a bug on that version.
Reedit:
Try with this, it worked for me:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.navigationItem.hidesBackButton = NO;
}
Use the UINavigationControllerDelegate method -navigationController:willShowViewController:animated:. You will implement this in view controller A and view controller B. In A you will set -hidesBackButton: to YES and alternatively to NO in view controller B.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
viewController.hidesBackButton = YES;
}
You can also use following sample code;
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = YES;
}
- (void) viewWillDisappear:(BOOL)animated{
self.navigationItem.hidesBackButton = NO;
}
If your view heirarchy is really such that View B should not show a back button but View C should, then the simplest way to get around this is to refactor your heirarchy. I'm thinking of the following alternative:
View A calls presentModalViewController:animated: on View B*, a UINavigationController whose view property is View B. View B* pushes View C onto its navigation stack in response to an event (or otherwise) from View B. If you need to jump back to
View A quickly then call dismissModalViewControllerAnimated: on View A. If you want to keep the state of View B* and C in memory then you could also keep another pointer to View B* somewhere so it doesn't go away when dismissed.