How to use didSelectRowAtIndexPath in objective-c - iphone

I have a Tab Bar with a navigation table view. My app crashes when I select a cell on my table view. I want a new viewcontroller to open when a cell is selected. My guess is that I am not pushing it correctly when didselectrowatindexpath is called. The app stays hung up for a few seconds then closes.
Does anything catch your eye with this code? Or do you have any sample code? I am using Xcode 3 with simulator 4.3.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = [indexPath row];
if (self.vailViewController == nil) {
VailViewController *vailView = [[VailViewController alloc] initWithNibName:#"View" bundle:nil];
self.vailViewController = vailView;
[vailView release];
}
vailViewController.title = [NSString stringWithFormat:#"%#", [resortsArray objectAtIndex:row]];
Ski_AdvisorAppDelegate *delegate = (Ski_AdvisorAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.resortsNavController pushViewController:vailViewController animated:YES];
[self.navigationController pushViewController:vailViewController animated:YES];
}
Thanks a lot!

if (self.vailViewController == nil) {
VailViewController *vailView = [[VailViewController alloc] initWithNibName:#"View" bundle:nil];
self.vailViewController = vailView;
[vailView release];
}
In this block of code is your view file actually named "View.xib" because if not this is your crash. With this error message
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle (loaded)' with name 'View''
So if you want to load this with initWithNibName and your files are named as such:
VailViewController.h
VailViewController.m
VailViewController.xib
you need:
VailViewController *vailView = [[VailViewController alloc] initWithNibName:#"VailViewController" bundle:nil];
However if it is a standard UIViewController subclass you should only need to do:
VailViewController *vailView = [[VailViewController alloc] init];
The above works if you just created a UIViewController subclass in Xcode and didn't change anything. The view controller knows how to load it's own view. If you had it create an .xib file for you and you deleted something in it or just arn't sure make sure the File's Owner is wired up to the root view in the .xib file in interface bulder.
I hope that helps.
Edit:
The error in your comments:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Pushing the same view controller instance more than once is not supported
is coming these two lines in your code:
[delegate.resortsNavController pushViewController:vailViewController animated:YES];
[self.navigationController pushViewController:vailViewController animated:YES];
I should ask if these are two different navigation controllers? if so why do they need the exact same view controller pushed to them?
I would remove one of them and then it should push the view controller without error.

What is aBookDetail? It seems like you might not be pushing the VailViewController onto the stack like you think you are. The code below might be more what you want. Also, does the debugger give you any error information about the crash?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = [indexPath row];
if (self.vailViewController == nil) {
VailViewController *vailView = [[VailViewController alloc] initWithNibName:#"View" bundle:nil];
self.vailViewController = vailView;
[vailView release];
}
self.vailViewController.title = [NSString stringWithFormat:#"%#", [resortsArray objectAtIndex:row]];
Ski_AdvisorAppDelegate *delegate = (Ski_AdvisorAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.resortsNavController pushViewController:self.vailViewController animated:YES];
}

Ensure the vailViewController is being retained.
#property (retain) VailViewController *vailViewController;
Also, is the resortsNavController off of Ski_AdvisorAppDelegate retained? How is that property defined?
Also, is the UITableViewController the rootController of the UINavigationController? If so, you should be able to call [self navigationController]
[[self navigationController] pushViewController:detailedView animated:YES];
I typically do this in my appDelegate:
_mainTableViewController = [[MainTableViewController alloc] init];
_navController = [[UINavigationController alloc] initWithRootViewController:_mainTableViewController];

Related

Calling on a Variable Defined in a Different File's Header?

I'm new to iOS development and have a question regarding variables. I'm attempting to write an application with a tab bar and navigation controller, the tab bar being the rootViewController. I've set up my application to include a .plist that includes my table view's behaviors, a UITableViewController, and a detail view controller. I continue to get one error in the implementation file of my initial table view controller as I defined the navigation controller in my App delegate.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the dictionary of the selected data source.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
//Get the children of the present item.
NSArray *Children = [dictionary objectForKey:#"Children"];
if([Children count] == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
}
else {
//Prepare to tableview.
IndustryTableViewController *industryTableViewController = [[IndustryTableViewController alloc] initWithNibName:#"IndustryTableViewController" bundle:[NSBundle mainBundle]];
//Increment the Current View
industryTableViewController.CurrentLevel += 1;
//Set the title;
industryTableViewController.CurrentTitle = [dictionary objectForKey:#"Title"];
//Push the new table view on the stack
[self.indNavControl pushViewController:industryTableViewController animated:YES];
industryTableViewController.tableDataSource = Children;
[industryTableViewController release];
}
}
I get the error when I push the new table view on the stack. I am importing the header file of my app delegate, but it still will not work, giving me the error, "Variable not defined in TableViewController.h". Is there a way for me to summon this variable, or is there a more effective way for me to solve this issue?
Thanks in advance, I can really use any help you give me.
You can get a pointer to your appDelegate and then get your variable like so:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
Another way would be to use dependency injection and inject the reference into your controller when you create it. So basically define an ivar in your controller to hold a reference and then do this:
[myController new];
[myController setController:otherController];
You are referring to navigationController with self.navigationController.
This implies that navigationController is a variable defined in your TableViewController class.
You should define this variable in your class and modify your init method of the tableviewcontroller to receive that variable.

Reusing detailed view controller in UISplitViewController

Basically when I implement a split view like the one presented in the Apple example 'MultipleDetailsViews', everything works fine, it allocates a new detailed view each time a row is selected. Here is the relevant code from the example:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
if (row == 0) {
FirstDetailViewController *newDetailViewController = [[FirstDetailViewController alloc] initWithNibName:#"FirstDetailView" bundle:nil];
detailViewController = newDetailViewController;
}
// ...
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailViewController, nil];
splitViewController.viewControllers = viewControllers;
// ...
}
But what I'm looking for is reusing detailed view controllers, that is lazily allocating a view controller when it is selected, and keeping a reference on it in my object. That way when another row is selected the view controller is not deallocated and when it is selected again it would be reused instead of allocating a new one. Here is the relevant code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
if (row == 0) {
if (self.firstDetailViewController == nil) {
FirstDetailViewController *newDetailViewController = [[FirstDetailViewController alloc] initWithNibName:#"FirstDetailView" bundle:nil];
self.firstDetailViewController = newDetailViewController;
[newDetailViewController release];
}
detailViewController = self.firstDetailViewController;
}
// ...
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailViewController, nil];
splitViewController.viewControllers = viewControllers;
// ...
}
self.firstDetailViewController is instanciated the first time the first row is selected then it is reused.
When I'm doing that it works well in landscape mode but in portrait mode after a few clicks in the popover menu it raises an exception: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Popovers cannot be presented from a view which does not have a window.'
So why I care? and why I don't want to reallocate view controllers? because in some of them I want to perform tasks that wouldn't be interrupted (killed) if the user navigate in a new detailed view while the task isn't completed yet.
Is there someone with an idea of what happens or with a working implementation of what I'm trying to achieve?
The view controllers are designed to be created and thrown away, if you need something to run in the background for longer then best move it into the master view controller or a separate object.
If you do however want to experiment with reusing a view controller this can be achieved by setting the viewControllers property on the new navigation controller with the previous detail controller saved in the viewDidLoad:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
UINavigationController* navigationController = (UINavigationController*)[segue destinationViewController];
// reuse existing controller
navigationController.viewControllers = #[self.detailViewController];
// update the detail controller as normal.
[controller setDetailItem:object];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}

Accessing DetailViewController from lower levels of rootviewcontroller in a navigation stack

I've been working on this for the past 11 hours and I think I've figured out what's going on but I can't figure out how to fix it. Basically I'm using the SplitView template for iPad. I have a RootViewController on the left and a DetailViewController on the right.
The main way this differs from the default template is that I'm using the RootViewControler's tableview to present a directory structure. That all works fine until I want to set a label on the DetailViewController from one of the deeper level in rootviewcontroller. I can set the label from the topmost rootviewcontroller but anything lower on the stack doesn't work.
I figure this is happening because every time you move down another level in the directory it pushes another instance of RootViewController onto the navigation stack and these new instances aren't connected to the DetailViewController. What I can't figure out is how to get the new instances to talk to the DetailViewController.
Here's the code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
detailViewController.detailItem = [NSString stringWithFormat:#"Row %d",indexPath.row];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *type = [[dataSource objectAtIndex:indexPath.row] objectAtIndex:2];
if ([type isEqualToString:#"tFolder"]) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *path = [[dataSource objectAtIndex:indexPath.row] objectAtIndex:0];
UITableViewController *targetViewController = [[RootViewController alloc] initWithPath:path];
[self.navigationController pushViewController:targetViewController animated:YES];
[targetViewController release];
} else if ([type isEqualToString:#"tFile"]) {
NSLog(#"Setting title");
detailViewController.detailItem = [NSString stringWithFormat:#"Row %d",indexPath.row];
}
}
Basically what should happen is if the user taps on a cell that is a folder it will push a new instance of RootViewController onto the navigation stack and display the contents of that directory in it. If the user clicks on a file it should set the detailItem to the name of that file (currently it's just placeholder code) which DetailViewController will then take and change a label on it's view to the file name.
The first detailViewController.detailItem at the top works but only if you're in the topmost RootViewController, the second at the bottom never works because it's only ever called in the lower levels on the stack.
Got it working using
RootViewController* topController = [self.navigationController.viewControllers objectAtIndex:0];
topController.detailViewController.detailItem = [NSString stringWithFormat:#"Row %d",indexPath.row];
Found the solution in this question: Problems accessing rootviewController of navcontroller iphone
I had tried using navigationController.topViewController but it would still return nil. However, using objectAtIndex:0 works.

iPhone SDK: pushViewController crashing

I am having problems with getting a detail view to load using pushViewController. At first, I thought pushViewController was not working. BUT, then I tried using a different view controller and it worked. I can tell from tracing that the problem view controller is never loaded at all. In other words, I tried to eliminate the possibility that there was some error in the view controller by NSLoging in that object and I never see anything.
Does anyone have any ideas?
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
/*
NSLog(#"hsitkjsdfkljlkd");
if (childController == nil)
childController = [[salesViewController alloc] initWithNibName:#"salesView" bundle:nil];
//NSUInteger row = [indexPath row];
[self.navigationController pushViewController:childController
animated:YES];
*/
/*
//modal = blocking
salesViewController *otherVC = [[salesViewController alloc] initWithNibName:#"salesView" bundle:nil];
//must set sale type
otherVC.strSaleType = #"Voice";
[self presentModalViewController: otherVC animated:YES];
//No close. By design, it is up to the otherVC to close itself
*/
//tipCalcViewController *detailViewController = [[tipCalcViewController alloc] initWithNibName:#"tipCalcView" bundle:nil];
salesViewController *detailViewController = [[salesViewController alloc] initWithNibName:#"salesView" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
Just Check the
- (void)viewWillAppear:(BOOL)animated
{
}
of the salesViewController.
you are doing something wrong in this..
put the debugging point in the viewWillAppear and run it. you can get the error line..
just try it......Surely it will work for u...
salesViewController *detailViewController = [[salesViewController alloc] initWithNibName:#"salesViewController" bundle:nil];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
Also make sure you are giving the IBOutlet connection to UIView.
In my case I had several IBOutlets that I removed from the Header file and forgot to remove the connection to these non-existing outlets in Interface Builder. So removing the obsolete outlets fixed the problem in my case.
When you're pushing from ViewController1 to ViewController2 then the code will be used like this so try this code,
ViewController2 *vw2=[[ViewController2 alloc]initWithNibName:#"ViewController2" bundle:nil];
[self.navigationController pushViewController:vw2 animated:YES];
The Above code may be written on Click event of button or on didSelect delegate of UITableView

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];
}
}