How to disable a DetailViewController UIBarButtonItem from RootViewController? - iphone

i have a main table view. and a DetailView. when cell is clicked, DetailView of that cell comes which shows details of that cell. DetailView has two buttons next and previous. I wanna know how to disable a detail view button from RootViewcontroller.m. code looks like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DetailViewController *nextController = [[DetailViewController alloc] init];
int storyIndex = [indexPath indexAtPosition:[indexPath length] -1];
nextController = [nextController initWithObjectAtIndex:storyIndex inArray:stories];
NSString *storyTitle = [[stories objectAtIndex:storyIndex] objectForKey:#"title"];
nextController.title = #"Details";
UIBarButtonItem *tempButtonItem = [[[UIBarButtonItem alloc] init] autorelease];
tempButtonItem.title = #"Back";
self.navigationItem.backBarButtonItem = tempButtonItem ;
nextController.sTitle = storyTitle;
[self.navigationController pushViewController:nextController animated:YES];
[nextController release];
}
i have already tried nextController.next.enabled=NO and [nextController.next setEnabled:NO] after this line:
[self.navigationController pushViewController:nextController animated:YES];
where next is the UIBarButtonItem name which is in DetailViewController.
can anybody tell me how to disable that button.
thanx in advance

Viewcontrollers and views are not loaded at the same time. What this means is that when you instantiate an object of DetailViewController in your case, the views are not drawn(and if you are using Nib's) loaded, this is part of the lazy loading concept.
So the first time you send the message setEnabled = NO, the object will be nil(sending messages to objects that are nil is allowed in Objective C).
Example:
[nextController setEnabled:NO] is equal to [nil setEnabled:NO] and this is surely not what you want.
The next time, unless a memory warning and the views are unloaded, the views will be in memory and the reference to the button will no longer be nil so the second time you invoke it, it will work.
And add the code line above the pushViewController:animate
If you want to initialize the button to be disable you may put this code in the viewDidLoad/viewWillAppear depending on the context of your application.
This is only one possible solution.
Edited answer to request in comment:
In your initializer method in the DetailviewController add this:
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(backToRoot)] autorelease];
- (void)backToRoot {
[self.navigationController popToRootViewControllerAnimated:YES];
}
and also add the method-signature to your headerfile.
Reference to the UINavigationController: http://developer.apple.com/library/ios/#documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html

Related

Pushing and Popping View Controllers

I have a navigation view controller which is a table view and then from the table view if i select a row it goes into detail view page. When i go to the detail view page i retrieve some information from the server and if the server does not respond then i get an alert view pop up which appears in front of my parent navigation view. now when press ok on the alert view and click on another row and it does go into my "didselectview" method but does not go to my detail view page. Would anyone know why? Code given below.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Navigation logic may go here. Create and push another view controller.
Item *selectedItem = (Item *)[self.fetchedObjectsArray objectAtIndex:indexPath.row];
NSString * urlString = [CONST_FEED_DISCRIPTION_URL stringByAppendingString:selectedItem.guid];
NSDate * dateString = selectedItem.date;
JsonViewController *jsonViewController = [[JsonViewController alloc] initWithURLString:urlString date:dateString];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStyleBordered
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backButton;
self.navigationController.navigationBar.tintColor = [UIColor colorWithHexString:CONST_NAVIGATIONBAR_COLOR];
[self.navigationController pushViewController:jsonViewController animated:YES];
[backButton release];
[jsonViewController release];
}
The first time around you get the alert view (which is an error message). After that nothing happens when u click on a row. but i know it goes into the method given below. would anyone know why? Does it have something to do with me pushing views? and not popping them?
Defer the push until the next runLoop, to let the tableView return:
dispatch_async(dispatch_get_main_queue(), ^ {
NSLog(#"Going to push...");
NSLog(#"...view Controller %#", jsonViewController);
[self.navigationController pushViewController:jsonViewController animated:YES];
[jsonViewController release];
NSLog(#"Just pushed %#", jsonViewController);
) );
Just to be clear, remove both these lines from the existing code:
[self.navigationController pushViewController:jsonViewController animated:YES];
[jsonViewController release];

iOS: Why isn't this View Controller getting pushed onto the Navigation Controller's stack?

I have a modal view that is a Navigation Controller. When one of the rows in its UITableView gets tapped, the correct View Controller for that row should be initialized and pushed onto the Navigation Controller's stack (so that the screen now shows that View Controller). But it's not working. I've been trying to debug it for a while, and it appears that the Navigation Controller's retain count is 0 at the time pushViewController is called. I assume that means it has been deallocated, and that this is the root of the problem. But I can't figure out why.
In the following code, AddSportDelegate.m presents the modal view that contains the necessary Navigation Controller (_addItemNavController) initialized with the necessary AddItemTableViewController. Tapping on one of the rows of the Table View managed by AddItemViewController calls the showAddItemDataView: method of AddSportDelegate, which in turn should push the correct ViewController onto the _addItemNavController stack. But, as I note in a comment in the code, the retain count of _addItemNavController at that moment is 0.
Note: I realize this code has memory leaks. I deleted some release lines for the sake of brevity. I also haven't included the code for the view controller that is supposed to be getting pushed, since it doesn't have anything at the moment beyond a UILabel identifying that it is the right View Controller.
AddItemDelegate.m
#synthesize addItemNavController = _addItemNavController;
- (void)showAddItemViewController:(UIViewController *)viewController
{
_parentVC = viewController;
[_parentVC retain];
tc = [[AddItemTableViewController alloc] init];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonSystemItemDone target:self action:#selector(cancelAdd)];
tc.navigationItem.leftBarButtonItem = cancelButton;
tc.title = #"Select a Category";
_addItemNavController = [[AddItemNavController alloc] initWithRootViewController:tc];
tc.superViewController = _addItemNavController;
[_parentVC.navigationController presentModalViewController:_addItemNavController animated:YES];
}
- (void)showAddItemDataView:(SportCategory *)category
{
[category retain];
UIViewController *vc;
if (category.name == #"Soccer") {
vc = [[AddSoccerDataViewController alloc] init];
}else{
vc = [[AddBaseballDataViewController alloc] init];
}
//retain count already 0
NSLog(#"retain count: %i", [_addItemNavController retainCount]);
[_addItemNavController.navigationController pushViewController:vc animated:YES];
}
AddItemTableViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
_addItemDelegate = [[AddItemDelegate alloc] init];
SportCategory *soccer = [[SportCategory alloc] initWithCategoryName:#"Soccer"];
SportCategory *baseball = [[SportCategory alloc] initWithCategoryName:#"Baseball"];
_categories = [[NSArray alloc] initWithObjects:soccer,baseball,nil];
[self.tableView reloadData];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SportCategory *selectedCategory = [_categories objectAtIndex:[indexPath row]];
[_addItemDelegate showAddItemDataView:selectedCategory];
}
I am going to take a shot at this.
if (category.name == #"Soccer")
I come from a java background, but I know a little objective - c. I thought you can't compare strings with == which would mean your view controller was never created. Maybe try a isEqualToString method.
That is my only thought, I could be wrong. But Best of Luck.
The '==' operator isn't the good way to compare strings, but anyway your code should fall into the else part.
About your question, _addItemNavController must be nil because your NSLog prints 0 for its retain count.
Is the method -(void)showAddItemViewController:(UIViewController *)viewController called somewhere ?
Your view controller doesn't seem to be initialized.
A bit of sleep helped me find the problem. There were actually two:
1) The final line in AddItemDelegate read:
[_addItemNavController.navigationController pushViewController:vc animated:YES];
However, _addItemNavController IS the navigation controller, so the '.navigationController' part needed to be deleted.
2) I also needed to assign tc.addItemDelegate to self in showAddItemViewController.

TableView help pushing a custom view

So I am trying to simple traverse to the next level of a table view by doing this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 1) {
FiltersController *aFiltersCont = [[FiltersController alloc] init];
aFiltersCont.displayedFilters = [appDelegate.groupedBusiness allKeys];
aFiltersCont.currentLevel = self.currentLevel + 1;
[self.navigationController pushViewController:self animated:YES];
}
}
is there any reason why this would not be pushing the controller? I had a similar problem before, but solved it by displaying the view modally. However, this time, this is in a popover and needs to slide to the next screen inside that popover. Any Ideas? Thanks in advance.
OK I am going to put some more source up here to try and help...
Inside the main view controller I have this code to make the popover from a button:
// Create and configure the filters controller.
FiltersController *aFiltersController = [[FiltersController alloc] initWithStyle:UITableViewStylePlain];
self.filtersController = aFiltersController;
filtersController.appDelegate = self.appDelegate;
UINavigationController *filtersNavController = [[UINavigationController alloc] initWithRootViewController:filtersController];
UIPopoverController *filtersPopover = [[UIPopoverController alloc] initWithContentViewController:filtersNavController];
self.filtersPopoverController = filtersPopover;
filtersPopoverController.delegate = self;
and then I have the code I first posted in my filtersController class. Does that help at all?
[self.navigationController pushViewController:self animated:YES];
Should be
[self.navigationController pushViewController:aFiltersCont animated:YES];
If you are just displaying your sublcass of UITableViewController inside a UIPopoverController, the UINavigationController will not be created for you automatically. You may need to modify the code where you are creating the UIPopoverController with something like this:
MyTableViewController *table = [[[MYTableViewController alloc] init] autorelease];
UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:table] autorelease];
myPopover = [[UIPopoverController alloc] initWithContentViewController:nav];
Then you should be able to push and pop navigation controllers on the stack no problem.
you are pushing self which is a reference to the current view controller. you should change the line with the push to the following if aFiltersCont is the viewController you are trying to drill to.
[self.navigationController pushViewController:aFiltersCont animated:YES];
You might be confused about the index. The first index is 0 not 1, so maybe you want indexPath.row == 0?
Also, you should release aFiltersCont before returning (you're leaking a FiltersController and anything it owns every time you run this method.
So maybe this?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
FiltersController *aFiltersCont = [[[FiltersController alloc] init] autorelease];
aFiltersCont.displayedFilters = [appDelegate.groupedBusiness allKeys];
aFiltersCont.currentLevel = self.currentLevel + 1;
[self.navigationController pushViewController:aFiltersCont animated:YES];
}
}

How do I pass an NSString through 3 ViewControllers?

hey, I'm currently using the iPhone SDK and I'm having trouble passing an NSString through 3 views
I am able to pass an NSString between 2 view controllers but I am unable to pass it through another one. My code is as follows...
`- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)index`Path {
NSString *string1 = nil;
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"items"];
string1 = [array objectAtIndex:indexPath.row];
//Initialize the detail view controller and display it.
ViewController2 *vc2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:[NSBundle mainBundle]];
vc2.string1 = string1;
[self.navigationController pushViewController:vc2 animated:YES];
[vc2 release];
vc2 = nil;
}
in the "ViewController 2" implementations I am able use "string1" in the title bar by doing the following....
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = string1;
UIBarButtonItem *addButton = [[[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:#"icon_time.png"]
style:UIBarButtonItemStylePlain
//style:UIBarButtonItemStyleBordered
target:self
action:#selector(goToThirdView)] autorelease];
self.navigationItem.rightBarButtonItem = addButton;
}
but I also have a NavBar Button on the right side that I would like to push a new view
- (void)goToThirdView
{
ViewController3 *vc3 = [[ViewController3 alloc] initWithNibName:#"ViewController3" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:NESW animated:YES];
vc3.string1 = string1 ;
[vc3 release];
vc3 = nil;
}
How do I pass on that same string to the third view? (or fourth)
What you have should work exceptthat you want to set the string in vc3 before you push it onto the stack to ensure it is present when the view and the nav bar draws. That is the way you have it in the vc2 that works.
However, as a matter of application design, it is poor practice to pass values directly between view controllers. Ideally, you want your view controllers to be standalone and able to function regardless of which other controller did or did not precede it. (This becomes really important when you need to resume an app back to the point where it was interrupted.) If make the view controllers interdependent your app will grow tangled and complex as it grows larger.
The best way to exchange data between views is to park the data in a universally accessible place. If it is application state information put it in user defaults or you can put in an attribute of the app delegate. If it is user data, then it should go in a dedicated data model object (which is either a singleton or accessible through the app delegate.)
You may find code sample from a question I asked earlier.

How do I add a button to my navigationController's right side after pushing another view controller in?

So, immediately after pushing a view controller to my tableView,
// Override to support row selection in the table view.
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here --
// for example, create and push another view controller.
AnotherViewController *anotherViewController =
[[AnotherViewController alloc] initWithNibName:#"AnotherView" bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
Ok, so that makes another view slide on, and you can go back to the previous view ("pop" the current view) by clicking the button that automatically appears in the top left corner of the navigation bar now.
Ok, so SAY I want to populate the RIGHT SIDE of the navigation bar with a DONE button, like in the "Notes" app that comes with the iPhone. How would I do that?
I tried code like this:
UIBarButtonItem * doneButton =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector( doneFunc ) ];
self.navigationController.navigationItem.rightBarButtonItem = doneButton ; // not it..
[doneButton release] ;
doneFunc is defined, and everything, just the button never appears on the right side..
The code you posted should work fine, I think. Question is, where did you put it? I would put it into the -viewDidLoad method of the view controller you're pushing in. If you need different buttons depending on the content you're showing then you could do it in the -viewWillAppear: method.
Update: Actually, I think you need to change
self.navigationController.navigationItem.rightBarButtonItem = doneButton;
to
self.navigationItem.rightBarButtonItem = doneButton;
AH. You have to do:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
AnotherViewController *anotherViewController =
[[AnotherViewController alloc] initWithNibName:#"AnotherView" bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
UIBarButtonItem * doneButton =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector( doneFunc ) ];
anotherViewController.navigationItem.rightBarButtonItem = doneButton ;
[doneButton release] ;
}
Who'da thunk it.