back button at root controller of uinavigationcontroller - iphone

I have a UINavigationController application with a root view controller and each time I am pushing a view controller to the stack. Let's say the stack is A B C D where A is the root view controller here. The issue is that when I am at view controller D and do a popToRootViewController it went back to A but A has a back button on it. When I click on back the back just slides in and disappear, why is this happening?
EDIT: I am actually subclassing my UINavigationController so that I can set my rootViewController as follows:
#import "CustomNavigationController.h"
#implementation CustomNavigationController
#synthesize fakeRootViewController;
//override to remove fake root controller
-(NSArray *)viewControllers {
NSArray *viewControllers = [super viewControllers];
if (viewControllers != nil && viewControllers.count > 0) {
NSMutableArray *array = [NSMutableArray arrayWithArray:viewControllers];
[array removeObjectAtIndex:0];
return array; }
return viewControllers; }
//override so it pops to the perceived root
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated {
//we use index 0 because we overrided “viewControllers”
((UIViewController *)[self.viewControllers objectAtIndex:0]).navigationItem.hidesBackButton = YES;
return [self popToViewController:[self.viewControllers objectAtIndex:0] animated:animated]; }
//this is the new method that lets you set the perceived root, the previous one will be popped (released)
-(void)setRootViewController:(UIViewController *)rootViewController {
rootViewController.navigationItem.hidesBackButton = YES;
[self popToViewController:fakeRootViewController animated:NO];
[self pushViewController:rootViewController animated:NO]; }
- (void)dealloc {
self.fakeRootViewController = nil;
[super dealloc]; }
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
UIViewController *fakeController = [[[UIViewController alloc] init] autorelease];
self.fakeRootViewController = fakeController;
NSMutableArray *array = [NSMutableArray arrayWithArray:[super viewControllers]];
[array insertObject:fakeController atIndex:0];
self.viewControllers = array;
}
return self; }
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use. }
#pragma mark - View lifecycle
/* // Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView { }
*/
/* // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad]; }
*/
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil; }
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait); }
#end
MORE UPDATE:
So after I set my rootViewController and then push a viewController and then tried to call popToRootViewController from that view, it all worked fine. However, if I push another viewController after the second one and then call the popToRootViewController, now I can see that weird back button on the root.

I too face the same problem. So in your root controller assign leftBarButtonItem equal to nil.
self.navigationItem.leftBarButtonItem = nil;
If your root controller reusing in the program. Then you need to check ->
BOOL needBackBarButton = (1 < [self.navigationController.viewControllers count]) ? YES : NO ;
if (! needBackBarButton)
{
self.navigationItem.leftBarButtonItem = nil;
}
For other controllers
if (needBackBarButton)
{
// Create custom navigationItem here.
}
else
{
self.navigationItem.leftBarButtonItem = nil;
}

Related

iPhone - self.view is nil in initWithNibName subclass, and viewDidLoad not called

I have this cascade :
Somewhere in the app
- (void) go
{
MyCustomViewController* controller = [[MyCustomViewController alloc] init];
controller.delegate = self;
[controller run];
}
in MyCustomViewController
- (id) init
{
// there is an if statement here in real to choose another XIB if needed
// but I only display here the code that is called in my tests
self = [super initWithNibName:#"MyXIB" bundle:nil];
if (!self) return nil;
self.delegate = nil;
return self;
}
- (void) run
{
// do things with self.view
}
MyCustomViewController inherits GenericWindowController
in GenericWindowController
/*- (id) initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
{
if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) return nil;
self.view.userInteractionEnabled = NO; // THE APP CRASHES HERE ! self.view is nil
...
return self;
}*/
// METHOD created following first answers : NOT CALLED
- (void) viewDidLoad
{
self.view.userInteractionEnabled = NO;
// and many other things done with self.view
}
MyXIB has its File's owner set to MyCustomViewController and the view is connected.
All files are included and checked into the project.
GenericWindowController is designed to make some standard stuff.
MyCustomViewController extends this stuff to work with a custom View, as designed in MyXIB.
Why self.view is nil in GenericWindowController ?
Why viewDidLoad is not called ?
A view controller should not try to access its view in initWithNibName:bundle:. It should wait until viewDidLoad.
self.view is only valid after viewDidLoad -- in init... it is still nil.
Incredible !!! The problem was because of a missing localization for the XIB. Added the localization, and no problem anymore.

Unwanted double navigation bar

I made the navigation bar (top bar) appear/disappear when I tap the screen, and also lay on top of the background image. It worked, but with one problem: I've suddenly got two navigation bars! First, one with a back button named "Back", and when I press "Back" it pops up a new navigation bar with a back button named "Vinene", which is the title of the TableView it leads back to. Thats the one I want to keep. I think the issue is somewhere in the DetailViewController.m or in the didselectrowatindexpath in the MasterViewController.m. Hope someone can see the problem!
DetailViewController.m:
#interface WinesDetailViewController ()
#end
#implementation WinesDetailViewController
#synthesize wineDictionary;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationController.navigationBar.translucent = YES;
self.wantsFullScreenLayout = YES;
UITapGestureRecognizer *tap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideShowNavigation)] autorelease];
tap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tap];
}
- (void) hideShowNavigation
{
[self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)hidesBottomBarWhenPushed{
return TRUE;
}
#end
MasterViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *dictionary = [wine libraryItemAtIndex:indexPath.row];
if (winesDetailViewController == nil) {
// Init the wine detail view
winesDetailViewController = [[WinesDetailViewController alloc] init];
}
// Here you pass the dictionary
winesDetailViewController.wineDictionary = dictionary;
[self.navigationController pushViewController:winesDetailViewController animated:YES];
}
}
Usually, a recurring navigation bar like you describe is caused by something like pushing the same view controller twice. Can you check to ensure you're only pushing a single view controller on to your navigation stack (via breakpoints or logging?). Is it possible that winesDetailViewController is already on the navigation stack? You can also try logging the value of self.navigationController.viewControllers for a hint.
I would also suggest moving
self.navigationController.navigationBar.translucent = YES;
to viewWillAppear and
self.wantsFullScreenLayout = YES;
to your initializer (though I don't think this will solve your problem).

Set Delegate methods on child view controll

I am building an application which should allow the user to scroll through the images. Since I have many images (downloaded from the web), what i doing is I have UP and down button on the parent view, in addition i have the scroll view. Based on the selected option (up or down), I add the ImageClass ( created a class which extends UIViewController) view to the scroll view.
Now, on the selected view the user can mark a point or do any stuff.
The question is how from the parent view I can call methods of the Uiviewcontroller. I know how I can set the delegate methods but what I want is that parent controller can call any a method say redraw method which would redraw the entire view.
Code:
-(IBAction) down:(id) sender{
[scrollView2 removeFromSuperview];
}
-(IBAction) down :(id) sender {
[scrollView2 removeFromSuperview];
if(scrollView2 == nil)
scrollView2 = [[UIScrollView alloc] init];
[self.view addSubview:scrollView2];
[self.view sendSubviewToBack:scrollView2];
[scrollView2 setBackgroundColor:[UIColor blackColor]];
[scrollView2 setCanCancelContentTouches:NO];
scrollView2.clipsToBounds = YES;
// default is NO, we want to restrict drawing within our scrollview
scrollView2.indicatorStyle = UIScrollViewIndicatorStyleWhite;
// CSImageView is a class which of the type UIViewController
imageVie = [[CSImageView alloc] init];
[scrollView2 addSubview:imageVie.view];
[scrollView2 setContentSize:CGSizeMake(1500,1500)];
[scrollView2 setScrollEnabled:YES];
[imageView release];
}
Now, from the parent view controller I want to call say:
imageVie.redraw(); method
Code for CSImageView
#interface CSImageView : UIViewController {
NSNumber *imageId ;
}
#property (nonatomic, retain) NSNumber *venueId;
-(void) redraw;
#end
#implementation CSImageView
#synthesize imageId;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(void) redraw {
NSLog(#"I am ehre in the test function");
}
#end
Can you please help me on the same. I am not able to call the redraw method. Any input would be appreciated.
First, CSImageView should probably be a subclass of UIView, not UIViewController. You should keep a reference to your instance (i.e. as an instance variable or synthesized property) so you can access it from methods other than - down:.
Wow.
I can't say I follow what you've coded, but here's how you do it.
During this part...add a tag to the view
// CSImageView is a class type UIViewController
imageVie = [[CSImageView alloc] init];
imageVie.tag = 99 // Any number. Preferably a constant
[scrollView2 addSubview:imageVie.view];
...
Then on the parent
view controller. You can ...
-(IBAction) down:(id) sender {
CSImageView *view = [scrollView2 viewWithTag:99];
[view redraw];
... }
Something like that.

Odd Scrolling Issue ( UIScrollView )

I have searched endlessly to no avail on this, hopefully someone can help!
I have a UIScrollView on the left half of a UIView which loads in custom cells/subviews. The UIView is part of a UINavigation stack, and it's also loaded into a tab on a TabBar.
What happens is, if I start the app and begin scrolling right away it's very smooth. However if I start the app and wait 5-10 seconds, the UIScrollView is VERY laggy and choppy (and it stays like that). I would think that it would be a memory leak or something, but I can't seem to find anything.
Included is the code of the view where I'm loading the custom cells/subviews into the UIScrollView. There isn't any custom code in the cell subview. Oh, and there's only about 8-10 items: each item has a small (150x150) image, and 3 text fields - all opaque.
#import "ProductListViewController.h"
#import "ProductListLeftItemViewController.h"
#implementation ProductListViewController
#synthesize listScroll;
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:#"Products" ofType:#"plist"];
NSArray *products = [[NSArray alloc] initWithContentsOfFile:path];
//
int numProducts;
numProducts = [products count];
[listScroll setContentSize:CGSizeMake(500, (numProducts * 111))];
for (int i = 0; i < numProducts; i++) {
ProductListLeftItemViewController *cellItem = [[ProductListLeftItemViewController alloc] initWithNibName:#"ProductListLeftItem" bundle:nil];
cellItem.view.frame = CGRectMake(0, (i*111), 500, 111);
[self.listScroll addSubview:cellItem.view];
cellItem = nil;
[cellItem release];
}
[products release];
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
NSLog(#"Memory warning, ProductListViewController");
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[listScroll release];
[super dealloc];
}
#end
And as requested, the ProductListLeftItemViewController code:
#import "ProductListLeftItemViewController.h"
#implementation ProductListLeftItemViewController
#synthesize titleTxt, descriptionTxt, modelTxt, productThumb;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
titleTxt = nil;
descriptionTxt = nil;
modelTxt = nil;
productThumb = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[titleTxt release];
[descriptionTxt release];
[modelTxt release];
[productThumb release];
[super dealloc];
}
#end
For anyone that has this problem in the future, once I started testing things on the actual device, everything worked perfectly smooth. Maybe it was just something quirky with the simulator? I'll consider this resolved, even though I'm still going to look into it a little further.

insertSubview: AtIndex: does not work

I went through the material regarding this issue but still can't figure it out completely.
The main issue is that i am using this method but the view does not show...
The app is built out of two view - the main one (i added it as a subview to the main nib) -
Has of a text field where i write some text.
Has a button that when pressed, the action method loads a second view that has a label with some text in it.
the source code of the first viewController:
#import <UIKit/UIKit.h>
#class TxtRunnerViewController;
#interface Itai_s_text_app_take_2ViewController : UIViewController {
int sliderSpeed;
IBOutlet UITextField *textInput;
TxtRunnerViewController *trvc;
}
#property (nonatomic, retain) IBOutlet UITextField *textInput;
#property (retain, nonatomic) TxtRunnerViewController *trvc;
- (IBAction)sliderChanged:(id)sender;//speed of text show changed
- (IBAction)textFieldDoneEditing:(id)sender;//dor 'DONE' on keyboard
- (IBAction)backgroundTap:(id)sender;//for handling tapping on background
- (IBAction)textEmButtonPressed:(id)sender;
#end
the .m of the first ViewController:
#import "Itai_s_text_app_take_2ViewController.h"
#import "TxtRunnerViewController.h"
#implementation Itai_s_text_app_take_2ViewController
#synthesize textInput;
#synthesize trvc;
- (IBAction)sliderChanged:(id)sender
{
UISlider *slider = (UISlider *)sender;
sliderSpeed = (int)(slider.value + 0.5f);//setting the speed determinned by the usr in slider
NSLog(#"Slider value is %#", sliderSpeed);
}
- (IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
NSLog(#"our text input is %#", textInput.text);
}
- (IBAction)backgroundTap:(id)sender
{
[textInput resignFirstResponder];
}
- (IBAction)textEmButtonPressed:(id)sender
{
NSLog(#"button pressed");
if ([textInput.text length]==0)
{
NSString *msg = nil;
msg = #"Write text to transmit";
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Forgot something?"
message:msg
delegate:self
cancelButtonTitle:#"Back"
otherButtonTitles:nil];
[alert show];
[alert release];
[msg release];
}
else { //init
NSLog(#"HI!");
if (self.trvc == nil)
{
NSLog(#"if accepted");
TxtRunnerViewController *tr = [[TxtRunnerViewController alloc] initWithNibName:#"TxtRunner"
bundle:nil];
self.trvc = tr;
[tr release];
}
[self.view removeFromSuperview];
[self.view insertSubview:trvc.view atIndex:0];
NSLog(#"InsertAtIndex was operated...!");
//[self.view addSubview:tvc.view];
}
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
if(self.trvc.view.superview == nil)
self.trvc = nil;
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[trvc release];
[super dealloc];
}
The second View is simply empty (has a label added to it in it's nib file):
#import
#interface TxtRunnerViewController : UIViewController {
}
#end
It's .m file is the default one, added no code to it.
When i press the button in the first view - it's view disappears but the second view does not appear instead, only a blank white view appears, nothing on it.
Doe
If you push the TxtRunnerViewController it will completely hide the first one, but it still exist in background. (without removeFromSuperview)
TxtRunnerViewController *vw = [[TxtRunnerViewController alloc]initWithNibName:#"TxtRunner" bundle:nil];
[self.view pushViewController:vw.view animated:NO];
[vw release];
To remove the first view it's a bit more difficult. You would need to implement a delegate in RootViewController which will be fired after clicking the done Button of the first view. Then in your RootViewController some code is executed which displays the second view and removes the first view.
For a delegate sample open the Utility Application Sample when creating a new project and figure out how it works or search some examples online.
The line:
[self.view removeFromSuperview];
Removes the view from its super
then the call
[self.view insertSubview:trvc.view atIndex:0];
Adds trvc to it - but that view has been removed and not visible anymore.
You need to add trvc to the superview.