Update DetailView with MasterView in Master-Detail application - iPad - objective-c

I am developing a master detail view application for iPad. All I have done so far is to get the data populated in my Master view. All these are images which are stored in NSMutableArray. When a user clicks on any row, The corresponding image should appear in the detail view.
I saw so many examples on this and I figured out that I need to make changes in didSelectRowAtIndexPath and in setDetailItem and configureView. But I don't know what exactly I have to do in these methods. Also, my Master view should be hidden when I select any row in it. This is not happening. Also, in iPad master detail template, there is no seague between the Master view and detail view. I tried creating one but then the data is being displayed in Master view only and not in detail view. I want the detail view to be updated. Please let me know if anyone know how to get this done.

You just need to get value of parameter that you want to pass to detail view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.detailViewController) {
self.detailViewController = [[[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil] autorelease];
}
// Here you need to pass data object that you want to show in detail view Controller
// Object contains value of particular index in tableView.
self.detailViewController.detailItem = object;
[self.navigationController pushViewController:self.detailViewController animated:YES];
}

Related

Presenting correct View Controller instance depending on cell selection in UITableView

I understand this might be covered in parts in answers to other questions (see references) I've seen on the site, but due to my limited experience I haven't been able to understand each part as it relates to my code. Please forgive me for any duplications.
I have a PFQueryTableViewController (essentially a UITableViewController) called threadsViewController that sources cell information from a Parse backend. The table view consists of threads similar to what you would see on a web forum.
I then have a separate class postsViewController which is another PFQueryTableViewController that I wish to display a table of all the responses ('posts') to that particular thread.
The functionality I'm looking for is for a user tapping on a thread (left screen in the image) to be presented with a postsViewController (right screen) containing only those posts/responses related to that thread. (See basic diagram below).
What I do know from my research:
The Parse backend is established with classes for Thread and Post.
I'm probably going to use the didSelectRowAtIndexPath delegate method
What I need help with:
How can I know which thread cell a user tapped on, and then pass that on so that the postsViewController only displays posts from that Thread?
A layman's description of how to use indexPath.row etc
I understand how to complete PFQueries etc to get the data for the cells, I just don't know how to implement the navigation and how to tell postsViewController which posts to show.
In case it helps somehow, here is my implementation so far. I have tried addign a property to the postsViewController called fromThread to somehow store the thread but apart from that I'm out of ideas!
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
postsViewController *postsVC = [[postsViewController alloc] init];
postsVC.fromThread = //?
[self performSegueWithIdentifier:#"threadsToPosts" sender:self];
}
References
How to filter a Parse query by an tableview index?
Pass Index Number between UITableView List segue
didSelectRowAtIndexPath and prepareForSegue implementation
You're on the right track. You need know how to do two things: (1) access your parse objects by indexPath, and (2) push a new view controller in a navigation controller.
(1) is simpler: PFQueryTableVC provides a method called objectAtIndexPath: that does just what you need.
// indexPath is the indexPath parameter to the didSelectRow delegate method
PFObject *fromThread = [self objectAtIndexPath:indexPath];
(2) is simple, too. But more complicated because there are a couple ways to do it. Segue is the more modern way, but I think the old way is simpler, and certainly easier to describe in code. View Controllers are given storyboard ids on the "Identity" tab in the storyboard editor. Give your Posts-presenting VC a storyboard id like "PostVC".
To get a new instance, use that storyboard id as follows:
MyPostVC *postVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PostVC"];
// initialize it with the PFObject we got above
postVC.fromThread = fromThread;
// present it on the navigation stack
[self.navigationController pushViewController:postVC animated:YES];
And fromThread is just what the PostVC will need to form a query for posts associated with the selected thread.
You can pass data of cell to next ViewController in prepareForSegue with something like this
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"threadsToPosts"]) {
UINavigationController *navCon = segue.destinationViewController;
postsViewController *postsViewController = [navCon.viewControllers objectAtIndex:0];
// Whatever you are populating your tableView with
Thread *thread = [self.thread objectAtIndex:self.tableView.indexPathForSelectedRow.row];
postsViewController.thread = thread;
}
}

Why does an empty tableView check the number of sections but a non-empty one does not?

I have set up a demo application with a simple UITableViewController with no contents, but an 'Add' button in the toolbar. This launches a modal view controller which is again empty other than a 'cancel' button. The cancel button just tells its delegate (the UITableViewController) to dismiss the modal.
I then added an NSLog statement in the UITableViewController's numberOfSectionsInTableView method.
Ordinarily, when the table view controller loads I see two calls to numberOfSectionsInTableView. When I open and dismiss the modal (which returns to the UITableViewController) I see no further calls to numberOfSectionsInTableView.
However, if I return 0 from numberOfSectionsInTableView, in addition to the two calls on display, I also see an additional numberOfSections call when the modal is dismissed.
This only happens when numberOfSectionsInTableView returns 0, and I have added no additional code to my project besides that mentioned. This is easily verifiable by setting up a couple of controllers as I've described and modifying the result from numberOfSectionsInTableView.
My questions:
Why is the UITableView calling numberOfSectionsInTableView on return from a modal view?
Why is it only doing this if numberOfSectionsInTableView returns 0?
In addition to numberOfSectionsInTableView, the UITableViewController is also calling cellForRowAtIndex: when the modal is dismissed. In fact, it is attempting to display the new contents of its dataSource. How am I meant to manually animate a row insertion if the first row added is going to already be updated automatically? Shouldn't it be left to me to make sure that my UITableView is consistent with its dataSource?
What property is the UITableViewController checking to know that there is one or more sections (and therefore ask my delegate how many sections)? It can't be numberOfSectionsInTableView itself, since I would see it called whenever I return from the modal, not only when numberOfSections = 0.
From UITableViewController docs:
When the table view is about to appear the first time it’s loaded, the
table-view controller reloads the table view’s data... The
UITableViewController class implements this in the superclass method
viewWillAppear:
If you watch in the debugger, the second call upon app launch is from UITableViewController's viewWillAppear: implementation - specifically the part referred to above, where tableView is sent the reloadData message.
Now, the first call to numberOfSectionsInTableView: on launch is also from UITableViewController's implementation of viewWillAppear: but not directly from that implementation's call to -[UITableView reloadData]. I'm not sure what the first call is all about.
But, to your question, the call to numberOfSectionsInTableView: that happens when dismissing the modal has exactly the same call stack as the second call from applicationDidFinishLaunching:withOptions:. My hypothesis then is that UITableView interprets having zero sections as being in a state where it has not loaded at all. That does make some sense actually. I'd consider an "empty" table view to be one without any rows, but one without any sections seems almost "uninitialized" to me. Furthermore the UITableViewDataSource documentation implies UITableView has by default one section. Returning zero from this method would be inconsistent with that assumption of the docs as well.
Now, to your concern about animation - if you give the table an empty section to work with, you will be able to have full control over inserting the first row with whatever animation you'd like, and not be locked in to when you need to reload.
I think the moral of the story is, don't return zero sections unless you really, really need to for some reason. The title of your post refers to this table view being "empty" as well but I think it's clear the framework finds zero sections to not be empty but unloaded.
Hope this helps! And thanks for posting the sample project for me to play around with.
Perhaps the delegate just couldn't believe its eyes. But seriously, since a table view has to have at least one section, passing 0 doesn't make any sense. Why do it? You pass it an invalid argument and it gives you back a weird response. As to why it doesn't ask for number of sections when you pass 1, I think it's because it doesn't need to know at that point (coming back from the modal view controller) -- the table view has already been populated (if there were some data) and you haven't changed anything in the model, so it doesn't need to update.
I added a few lines to your example project to slide in a row each time you return from the modal view controller, which is what I think you're trying to do. I added an int property num for the return value of numberOfRowsInSection, added an array to populate the table, and a call to insertRowsAtIndexPaths from the modal view controller dismissal method.
- (void)viewDidLoad
{
_num = 0;
self.theData = #[#"one",#"two",#"three"];
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(addRecipe)];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"# sections requested");
//when 0, this fires on return from the modal. When 1, it does not.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"in numberOfRows in section");
return _num;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"in cellForRowAtIndexPath");
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
cell.textLabel.text = [self.theData objectAtIndex:indexPath.row];
return cell;
}
- (void)addRecipe
{
//create the modal and suscribe for delegate notifications
AddRecipeViewController *addRecipeController = [[AddRecipeViewController alloc]initWithStyle:UITableViewStyleGrouped];
addRecipeController.delegate = self;
//display the modal in a navigation controller
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addRecipeController];
[self.navigationController presentModalViewController:navController animated:YES];
}
- (void)addRecipeVC:(AddRecipeViewController *)addRecipeVC didAddRecipe:(NSString *)recipe
{
[self dismissModalViewControllerAnimated:YES];
_num += 1;
[self performSelector:#selector(addRow) withObject:nil afterDelay:.5];
}
-(void)addRow {
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:_num-1 inSection:0]] withRowAnimation:UITableViewRowAnimationRight];
}
The tableview checks number of sections when it's populating the table view with data!
Since the table can be divided into sections, it has to know specifically how many sections to divide it into.
When you reload the data, the number of sections is also checked.
Because every time the table view has to take action in accessing either the data of the table, like what row you tapped, and in what section, or populating the data table, the number of sections has to be known!
Hope this helped!

pushViewController is not pushing

I was using initWithNibName to push the cells to detail views but I want to use single storyboard rather than having numerous nib files. The below code is supposed to push the view to subcategoriesViewController when the cell is clicked. the cells are dynamic from json data.
Its not giving any errors. Do you think its an issue with navigation control? Just to test, I added a button on this view and it works; when I click it, it does push to next view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
subcategoriesViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"subcategoriesViewController"];
controller.CATNAME = [dict objectForKey:#"C_NAME"];
controller.CATNUMBER = [dict objectForKey:#"CAT_ID"];
[self.navigationController pushViewController:controller animated:YES];
// [controller release];
}
Okay I think I have found the cause; but don't know how to fix it.
The tableview in this controller is actually a subview of a viewController. I duplicated these files and created a new view which is a TableViewController. This code, when used in the TableViewController does the job, but it doesn't do it when used in a controller where the tableview is a subview of a ViewController.
I want the table to be a subview because I want to put an image above the table. So, I think the problem here is this line:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
It does not reference the table in the view. Can anyone tell me how to modify this so it would reference the table within the view?
If didSelectRow... is not being called then your view controller is probably not set as the delegate of your table view.
This is set up for you automatically when using a UITableViewController but you have to explicitly do it when using a standard view controller.
An answer to your problem is often that self.navigationController is in fact nil because there is no navigation controller for your view. You perhaps have to get a pointer to the navigation controller of your projet and push your view on it.

Unable to display the detail view on selecting a row in TableView with a UITabBar

I am using a Navigation based application.The third view in my flow has a UITabbar added.On clicking one of the tabs a TableView is displayed.On selecting a row of this TableView I wish to display another view.
My code works fine to the point of displaying the different views on clicking the tabs.When I select a row of the TableView nothing is displayed.I use a plist to populate the tableView. I added the following code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
PropertyRegistration *dvController = [[PropertyRegistration alloc] initWithNibName:#"PropertyRegistration" bundle:[NSBundle mainBundle]];
//dvController.selectedCountry = selectedCountry;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}
Can someone help?
I assume the third view controller is a UITabBarController which is working fine. But your root controllers in each tab, are you sure that they are UINavigationControllers? They (or at least the one containing your table view) need to be.
If your third view controller is not a UITabBarController, I'd recommend that you change it to be so. Then your structure would be something like:
UINavigationController: pushes through two UIViewControllers, then a UITabBarController (your third).
UITabBarController contains some view controllers, one or more of which is a UINavigationController. This navigation controller has a UIViewController as it's root view controller, and has other controllers pushed onto it.

Is there a way to change sectioned tableview to drilldown tableview?

I have a sectioned tableview with a plist wich is an array filled with dictionaries.
In my app all sections and cells are shown on the first view.
But now I need to change it to look like this: sections have to become cells(with names of sections). When you press this cell the cells contained in section have appear.
Is there a way to do it without too much rewriting the code?
Thanks and sorry for the noob question :o)
I understand, that you still want to use one tableview, where cells are hidden for all sections except one. In that case you can do this:
Implement -tableView:headerForSection: and place a button on the view that you will return. Write the sections number on the buttons tag. Add an action to the button with parameter (UIButton *)sender: `-(void) headerPressed:(UIButton *)sender
You need to implement -tableView:heightforHeaderInSection: as-well.
-(void) headerPressed:(UIButton *)sender writes the senders tag to a member integer and reloads the tableview.
in -tableview:numberOfRowsInSection: you return 0 if the sections int is not equal to the member integer you saved. If it is equal, return the number of rows in that section.
You will need to create two table view classes. One for the one that holds the section names, and the other that holds the rows of each section. In the first one, retrieve the section names from the plist and populate the rows in the table accordingly. Then in the - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method you need to push another view controller onto the navigation stack(something like this-provided in the template code):
// Navigation logic may go here. Create and push another view controller.
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
Then for the other table view class you populate it by reading in the info from the plist that corresponds to the tapped section. Hope this helps.