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.
Related
I'm creating a table view application.
I have to display a web view when a cell in the table view is touched.
But the tableView's navigation controller is showing a nil value.
And it is not pushing to the detailsViewController.
Anyone please help me to resolve this issue.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NewsDetailsViewController *detailsVc=[[NewsDetailsViewController alloc]initWithNibName:#"NewsDetailsViewController" bundle:nil];
NSString *launchURL = [nf.newsStories [indexPath.row] objectForKey:#"link"];
detailsVc.url = launchURL;
NSLog(#"View controllers before pushing : %#",self.navigationController.viewControllers);
[self.navigationController pushViewController:detailsVc animated:YES];
NSLog(#"View controllers after pushing : %#",self.navigationController.viewControllers);
}
NavigationController Value in debugger
I guess the problem is with creating your controller which this line
NewsDetailsViewController *detailsVc=[[NewsDetailsViewController alloc]initWithNibName:#"NewsDetailsViewController" bundle:nil];
Try to replace it with this one
NewsDetailsViewController *detailsVc= [[NSBundle mainBundle] loadNibNamed:#"NewsDetailsViewController"
owner:self
options:nil][0];
Anyway I advise you to move to using Storyboards and Segues
I'm working on a navigation-based application.
My rootViewController contains 3 cells
-When the first one is pressed a UIViewController is pushed (THIS WORKS)
-The problem is with the 2nd and 3rd cells that are supposed to push a UITableViewController
The app is running with no errors and it is NOT crashing at all, but when i navigate to the tableview, an empty table is viewed with no elements in it.
Is the problem with this part of the code?? :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *detailViewController = [[UIViewController alloc] initWithNibName:#"Introduction" bundle:nil];
detailViewController.title=#"Introduction";
UITableViewController *myPictures = [[UITableViewController alloc] initWithNibName:#"Pictures" bundle:nil];
myPictures.title=#"Pictures";
UITableViewController *myImages = [[UITableViewController alloc] initWithNibName:#"ImagesViewController" bundle:nil];
myImages.title=#"Images";
// Pass the selected object to the new view controller.
if (0 == indexPath.row)
[self.navigationController pushViewController:detailViewController animated:YES];
if (1== indexPath.row)
[self.navigationController pushViewController:myPictures animated:YES];
if (2== indexPath.row)
[self.navigationController pushViewController:myImages animated:YES];
[detailViewController release];
[myPictures release];
[myImages release];
What you're doing it very wrong (apart from your original problem). Why are you instantiating each view controller and then only using the one based on the current 'cell selection'? This will slow your app down depending on how much time it would take to load each of these separate views. You should only instantiate the relevant view controller inside the "if (indexPath.row == 2) {" block.
Apart from that there are many things wrong about your approach. What you are doing will never work since instantiating a generic UITableViewController (even if you supply your own nib) will obviously only show you an empty view. You need to have a class of your own which the nib is tied to as a delegate which will then supply the TableView with data.
I believe when you created these nib files (for example "Pictures") xcode also give you a 'PicturesViewController.h and PicturesViewController.m" files? If so, you need to write the appropriate code there and ensure the Tableview in the "Pictures" nib file has its 'datasource' and 'delegate' set to 'PicturesViewController'. Then when you want to show that view do this instead:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 1) {
...
} else if (indexPath.row == 2) {
PicturesViewController *myPictures = [[PicturesViewController alloc] initWithNibName:#"Pictures" bundle:nil];
[self.navigationController pushViewController:myPictures animated:YES];
[myPictures release];
}
}
I want to build an app for playing local audio files on the iPhone but I'm stuck with some of my codes.
I'm wondering how you can push a view, come back to the uitableviewcontroller and use a button ( like the "NOW PLAYING" button in the media player) to come back to the view without pushing any new string into it..
THANK YOU
What should I change from my codes ?
in the uitableviewcontroller..
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
selectedSong = [directoryContent objectAtIndex:indexPath.row];
NSString *storyLin = [[directoryContent objectAtIndex:[indexPath row]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
patch = [NSString stringWithFormat:#"/%#", storyLin];
myDetViewCont = [[mPlayerViewController alloc] initWithNibName:#"mPlayerViewController" bundle:nil];
myDetViewCont.myProgLang = selectedSong; // assigning the correct value to the variable inside DetailViewController
[self.navigationController pushViewController:myDetViewCont animated:YES];
[myDetViewCont release]; // releasing controller from the memory
}
in mPlayerViewController.m
-(IBAction) backtoplayer{
myDetViewCont = [[mPlayerViewController alloc] initWithNibName:#"mPlayerViewController" bundle:nil];
}
If you have pushed a view onto the navigation controller, just pop it to review the view underneath.
That is, the view you push myDetViewCont should just be popped in the backtoplayer call.
- (IBAction)backToPlayer:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
To Add to what Mark said.
Once you've popViewControllerAnimated and the user want to push the same controller again, you just need to keep the mPlayerViewController rather than release it.
Such as:
if (!myDetViewCont)
{ // We only need to create it once.
myDetViewCont = [[mPlayerViewController alloc] initWithNibName:#"mPlayerViewController" bundle:nil];
}
myDetViewCont.myProgLang = selectedSong;
[self.navigationController pushViewController:myDetViewCont animated:YES];
// no longer release here, move the release call to the dealloc
I have a TableView setup as my "root view controller" and a detail view controller called "DetailController" to allow me to display the detail view for what the user selects in the main table view. The navigation is working correctly - when I select a row in my table the screen slides to the left and I'm presented with my detail view, but I can't update the detail view's UILabel text properties. It's like it is just ignoring my UILable set property methods (see code below) as it isn't giving me any errors or warnings.
I've tested that I do have the values I need when I hit the "tableView:didSelectRowAtIndexPath:" method, but for some reason I can't seem to update the detail view's UILabels' text properties. Using NSLog's I can see that I am getting the values and passing them correctly to the UILabels. However the detail view isn't showing the passed values in its UILabels.
The strange thing is that I CAN set the detail view's title property correctly, just not the UILabel properties, which is very confusing!
Here's my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
if (self.detailView == nil) {
DetailController *detail = [[DetailController alloc] initWithNibName:#"DetailController" bundle:nil];
self.detailView = detail;
[detail release];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSDictionary *dict = [[NSDictionary alloc] initWithDictionary:[theList getItem:cell.textLabel.text]];
[self.detailView.theID setText:#"LOOK AT ME NOW!"];
self.detailView.thePrice.text = [[dict objectForKey:#"PRICE"] stringValue];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:self.detailView animated:YES];
NSString *newTitle = [[NSString alloc] initWithString: #"Viewing Attributes for: "];
newTitle = [newTitle stringByAppendingString:cell.textLabel.text];
self.detailView.title = newTitle;
[dict release];
[newTitle release];
}
Thanks for any help on this! I love this community - you guys are awesome!
I Found An Answer! I think...
After much trial and error I was able to figure out a solution, but the solution doesn't entirely make sense to me and still leaves me with questions.
Siegfried suggested that I move my "pushViewController" message to the end of the method so that the last six lines of my code were changed to:
// Pass the selected object to the new view controller.
NSString *newTitle = [[NSString alloc] initWithString: #"Viewing Attributes for: "];
newTitle = [newTitle stringByAppendingString:cell.textLabel.text];
self.detailView.title = newTitle;
[dict release];
[newTitle release];
[self.navigationController pushViewController:self.detailView animated:YES];
}
When I moved it to the last line (as shown above) my program would crash with an EXC_BAD_ACCESS error. I still don't understand why moving that line of code to the end of my method caused the program to crash?
However, this got me thinking - I was able to successfully change the title of the new detail view, but not the view's UILabel properties. So I moved my pushViewController method back to where it was and then moved my methods for setting the new view's UILabels underneath the pushViewController method and BOOM! Everything worked! The new detail view was being pushed when the user clicked on the row in my table and its properties (title and UILabels) were all being set correctly.
Why does this work?
I remember reading about how the iPhone handles views, something called "lazy loading" or "lazy instantiation"? I think this means that I can't change the properties of my detail view UNTIL I've "pushed" it, which is when the object is actually created in memory and its properties become available?
If this is correct, that would kind of explain the error I was getting when I moved the pushViewController method to the end of my code - I was trying to change the title property on a view that wasn't instantiated. However, that doesn't completely make sense because I was trying to change the UILabel properties of the view in my original code, which was before the view was actually pushed and the program wasn't crashing?
So my code that is currently working looks like the following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
if (self.detailView == nil) {
DetailController *detail = [[DetailController alloc] initWithNibName:#"DetailController" bundle:nil];
self.detailView = detail;
[detail release];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSDictionary *dict = [[NSDictionary alloc] initWithDictionary:[theList getItem:cell.textLabel.text]];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:self.detailView animated:YES];
// Moved the "set property" methods to happen after the detail view is pushed
[self.detailView.theID setText:#"LOOK AT ME NOW!"];
self.detailView.thePrice.text = [[dict objectForKey:#"PRICE"] stringValue];
NSString *newTitle = [[NSString alloc] initWithString: #"Viewing Attributes for: "];
newTitle = [newTitle stringByAppendingString:cell.textLabel.text];
self.detailView.title = newTitle;
[dict release];
[newTitle release];
}
I like that the code is working, but I would really like to understand what was and is actually happening. Can anyone offer additional insight here?
Thank you!
Move
[self.navigationController pushViewController:self.detailView animated:YES];
to last line.
You may try doing:
NSString *strText = [NSString stringWithFormat:#"%#",[[dict objectForKey:#"PRICE"] stringValue]];
self.detailView.thePrice.text = [NSString stringWithFormat:#"%#",strText];
Check wheather strText is containing the expected value and is not nil.
First of all I know this is a long question. REST ASSURED I have tried to figure it out on my own (see: StackOverflow #2609318). This is driving me BATTY!
After trying and failing to implement my own EDIT feature in the standard moreNavigationController, I have decided to re-implement my own MORE feature.
I did the following:
Add a HOME view controller which I init with: initWithRootViewController
Add 3 other default tabs with:
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
resortsListViewController.title = [categoriesDictionary objectForKey:#"category_name"];
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"whatever.png"];
resortsListViewController.navigationItem.title=#"whatever title";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
localNavigationController.navigationBar.barStyle = UIBarStyleBlack;
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
[resortsListViewController release];
Those work when i add them to the tabbar. (ie: click on them and it goes to the view controller)
Then I add my own MORE view controller to the tabbar:
MoreViewController *moreViewController;
moreViewController = [[MoreViewController alloc] initWithNibName:#"MoreView" bundle:nil];
moreViewController.title = #"More";
moreViewController.tabBarItem.image = [UIImage imageNamed:#"more.png"];
moreViewController.navigationItem.title=#"More Categories";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:moreViewController];
localNavigationController.navigationBar.barStyle = UIBarStyleBlack;
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
[moreViewController release];
Then
tabBarController.viewControllers = localControllersArray;
tabBarController.moreNavigationController.navigationBar.barStyle = UIBarStyleBlack;
tabBarController.customizableViewControllers = [NSArray arrayWithObjects:nil];
tabBarController.delegate = self;
That creates the necessary linkages. Okay, so far all is well. I get a HOME tab, 3 category tabs and a customized MORE tab -- which all work.
in the MORE tab view controller I implement a simple table view that displays all the other tabs I have in rows. SINCE I want to be able to switch them in and out of the tabbar I created them JUST like i did the resortslistviewcontroller above (ie: as view controllers in an array). When I pull them out to display the title in the tableview (so the user can go to that "view") i simply do the following:
// [myGizmoClass CategoryArray] holds the array of view controller tab bar items that are NOT shown on the main screen.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
... etc...
UIViewController *Uivc = [[myGizmoClass plusCategoryArray] objectAtIndex:indexPath.row];
cell.textLabel.text = [Uivc title];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
THIS is where it falls through:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
UIViewController *tbi = [[myGizmoClass plusCategoryArray] objectAtIndex:indexPath.row];
NSLog(#"%#\n",[[tbi navigationItem ]title]);
[self.navigationController pushViewController:tbi animated:YES];
}
This is the error i get ("ATMs" is the title for the clicked tableview cell) so i know the Uivc title is pulling the correct title and therefore the correct "objectatindex":
2010-04-09 11:25:48.222
MouseAddict[47485:207] ATMs 2010-04-09
11:25:48.222 MouseAddict[47485:207]
*** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:
'Pushing a navigation controller is
not supported'
BIG QUESTION: How do i make the associated VIEW of the UIViewController *tbi show and get pushed into view?
I am GUESSING that the UIViewController is the correct class for this tbl .. i am not sure. BUT i just wanna get the view so i can push it onto the stack.
Can someone plz help?
To answer kovpas's question below: myGizmoClass is a singleton (apple's singleton myGizmo class. The array of viewcontrollers is stored in that just like it is in [localControllersArray addObject:localNavigationController]; (in the first code snippet above). AND it does put it in and pull it out correctly as evidenced by the fact that when i NSLOG the [Uivc title] the log prints ATMs. This means the plusCategoryArray is correctly storing and retrieving the viewController (if, indeed, that is what is being stored).
Pushing a navigation controller is not supported is really bothering me. Why would a viewController return a navigationController and is it possible to coerce the navigationController to get the "pushable" view out of it... or does the navigationController have some element in it that is the view?
From the error, it looks as if your Gizmo class has an array of UINavigationControllers, not UIViewControllers. So instead push with:
[self.navigationController pushViewController:[[tbi viewControllers] lastObject] animated:YES];
If the array is the same array as you called localControllers above, then this should work better. Or you could just create the array without the UINavigationControllers, they aren't needed if you are going to push them onto your more controller navigation controller.
I'm not sure, but it looks like this error appears when you are trying to push UINavigationController into another UINavigationController. Could you please provide an implementation of MyGizmoClass?