How do I drill down through a plist using UITableViews? - ios

I'm wanting to drill down through a plist using uitableviews to get the a specific school. The drill down goes state->district->school. I've created a plist, but am not 100% sure that the structure is the best. Also, I can get the first set of information active on the first tableview, but am not sure how to proceed from there. Will I need to create a tableview for each drill down(stateview, districtview, schoolview) or can I reuse a generic tableview since they will simple be lists? Below is what I have so far. Thanks for your help.
PLIST
<plist version="1.0">
<array>
<dict>
<key>districts</key>
<dict>
<key>District 1</key>
<array>
<string>School 2</string>
<string>School 1</string>
</array>
<key>District 2</key>
<array>
<string>School 3</string>
<string>School 4</string>
</array>
</dict>
<key>state</key>
<string>South Dakota</string>
</dict>
<dict>
<key>districts</key>
<array>
<string>District 1</string>
<string>District 2</string>
</array>
<key>state</key>
<string>Arkansas</string>
</dict>
<dict>
<key>districts</key>
<array>
<string>District 3</string>
<string>District 4</string>
</array>
<key>state</key>
<string>New York</string>
</dict>
</array>
</plist>
And here is my viewcontroller
#import "plistViewController.h"
#interface plistViewController ()
#end
#implementation plistViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
}
return self;
}
#synthesize content = _content;
-(NSArray *)content
{
if (!_content) {
_content = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"]];
}
return _content;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.content count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [[self.content objectAtIndex:indexPath.row] valueForKey:#"state"];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
}
#end

The best thing about UITableView is that it doesn't care what data it displays. It just asks its delegate a few different questions:
How many sections should I have?
How many rows will be in each section?
May I have a UITableViewCell for this _ Index Path?
So, you have to focus on making your delegate responses provide the correct data.
So, first split your plist up into manageable chunks. The UITableView prima-donna dataSource is an NSArray. Neatly maps to tableViews because of indexing logic.
That said, your first tableViewController plistViewController has good logic for displaying info. Specifically, you are querying the NSDictionary at array position x and asking for it to return its state object. 3 Dictionary objects, 3 strings returned. Nice.
So how do you go to the next level? Your tableView will help you here. It asks a specific question of its delegate:
What do I do when user touches Section Y Row X?
You're going to need to set up another UITableViewController subclass called DistrictViewController. In the header .h file, you are going to need to make a strong property to an NSDictionary object. like so:
//DistrictViewController.h
#interface DistrictViewController : UITableViewController
#property (nonatomic, strong) NSDictionary *districtDictionary;
#end
//DistrictViewController.m
#implementation DistrictViewController
#synthesize districtDictionary;
And there we have it. This class is now set up to keep track of 1 NSDictionary object. Now you just have to configure your table delegate methods to show you the data you want.
The first example, what would be in the top Row (index:0) of the NSArray, you have a dictionary that has 2 keys: District 1 and District 2. But this is a problem. NSDictionary doesn't map to TableViews quite as easily, because NSDictionary objects don't use indexes to work. Don't fret. NSDictionary has a method called allKeys, which will give you an array of each key in the dictionary. This is useful for when you will be receiving an NSDictionary from somewhere but not know what keys it contains beforehand.
So, the questions your tableView asks, let's answer them:
//How many sections will be in me: Let's just say 1 for now.
//How many rows will be in this section:
//Ask the NSDictionary how many keys it has:
NSArray *keyArray = [self.districtDictionary allKeys];
return [keyArray count];
//Give me a tableCell for index path X,Y
//First, get your array of keys back:
NSArray *keyArray = [self.districtDictionary allKeys];
//Next, find the key for the given table index:
NSString *myKey = [keyArray objectAtIndex:indexPath.row];
//Finally, display this string in your cell:
cell.textLabel.text = myKey;
After this, you'd do the same thing for the final view. Set up a viewController for schools and call it SchoolViewController and make set it up to be in charge of an NSArray. Just like before:
#interface SchoolViewController : UITableViewController
#property (nonatomic, strong) NSArray *schoolArray;
#end
#implementation SchoolViewController
#synthesize schoolArray;
In this view, it will be a lot like the first. You just have this viewController answer the table's questions like before:
How many sections? We need 1
How many rows? We need as many as in the array return [schoolArray count];
Give me a cell: cell.textLabel.text = [schoolArray objectAtIndex:indexPath.row];
The final piece that puts this all together is in the final question the table asks.
What do I do when a user touches a row?
In each view, look at this method signature:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
This is where you add your logic to hook things up. In the first view plistViewController, do this:
NSDictionary *topLevelDictionary = [self.content objectAtIndex:indexPath.row];
NSDictionary *allDistricts = [topLevelDictionary objectForKey:#"districts"];
DistrictViewController *dView = [[DistrictViewController alloc] initWithStyle:UITableViewStylePlain];
dView.districtDictionary = allDistricts;
[self.navigationController pushViewController:dView animated:YES];
In the second view, DistrictViewController do this:
NSArray *keyArray = [self.districtDictionary allKeys];
NSString *myKey = [keyArray objectAtIndex:indexPath.row];
NSArray *schoolArray = [self.districtDictionary objectForKey:myKey];
SchoolViewController *sView = [[SchoolViewController alloc]initWithStyle:UITableViewStylePlain];
sView.schoolArray = schoolArray;
[self.navigationController pushViewController:sView animated:YES];
I hope this helps you. I typed this all in a plain text editor. Hopefully there's no misspellings. You'll need to #import the associated viewControllers in each one! Good luck.

To create drill down tables:
you can do:
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *districts = [NSArray arrayWithObjects:#"district1", #"district2", #"district3", nil];
NSArray *states = [NSArray arrayWithObjects:#"NY", #"NJ", #"NO", #"StateOther1", #"StateOther2", nil];
NSArray *schools = [NSArray arrayWithObjects:#"", #"school1", #"school2", #"school3", #"school4", nil];
NSMutableDictionary *schoolSection = [NSMutableDictionary dictionary];
[schoolSection schools forKey:#"items"];
[schoolSection setObject:#"Shools" forKey:#"title"];
NSMutableDictionary *districtSection = [NSMutableDictionary dictionary];
[districtSection setObject:districts forKey:#"items"];
[districtSection setObject:#"Section" forKey:#"title"];
NSMutableDictionary *stateSection = [NSMutableDictionary dictionary];
[districtSection setObject:states forKey:#"items"];
[districtSection setObject:#"State" forKey:#"title"];
self.adresses = #[schoolSection, districtSection,stateSection];
}
Next:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.adresses.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *currentSection = [self.adresses objectAtIndex:section];
if ([[currentSection objectForKey:#"isOpen"] boolValue]) {
NSArray *items = [currentSection objectForKey:#"items"];
return items.count;
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
NSDictionary *currentSection = [self.adresses objectAtIndex:indexPath.section];
NSArray *items = [currentSection objectForKey:#"items"];
NSString *currentItem = [items objectAtIndex:indexPath.row];
cell.textLabel.text = currentItem;
return cell;
}
Next:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
NSDictionary *currentSection = [self.adresses objectAtIndex:section];
NSString *sectionTitle = [currentSection objectForKey:#"title"];
BOOL isOpen = [[currentSection objectForKey:#"isOpen"] boolValue];
NSString *arrowNmae = isOpen? #"arrowUp":#"arrowDown";
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0.0f, 0.0f, 320.0f, 50.0f);
button.tag = section;
button.backgroundColor = [UIColor brownColor];
[button setTitle:sectionTitle forState:UIControlStateNormal];
[button addTarget:self action:#selector(didSelectSection:)
forControlEvents:UIControlEventTouchUpInside];
[button setImage:[UIImage imageNamed:arrowNmae] forState:UIControlStateNormal];
return button;
}
Next:
- (void)didSelectSection:(UIButton*)sender {
//get current section
NSMutableDictionary *currentSection = [self.adresses objectAtIndex:sender.tag];
//get elements of section
NSArray *items = [currentSection objectForKey:#"items"];
//create array of indexes
NSMutableArray *indexPaths = [NSMutableArray array];
for (int i=0; i<items.count; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:sender.tag]];
}
//get current state of section is opened
BOOL isOpen = [[currentSection objectForKey:#"isOpen"] boolValue];
//set new state
[currentSection setObject:[NSNumber numberWithBool:!isOpen] forKey:#"isOpen"];
//animate of adding and deleting of cells
if (isOpen) {
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
} else {
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
}
//reload button image
NSString *arrowNmae = isOpen? #"arrowDown.png":#"arrowUp.png";
[sender setImage:[UIImage imageNamed:arrowNmae] forState:UIControlStateNormal];
}
And you can customize this table, like you need.
Example of drill down tables you can download here (click "Скачать" button)

You should pass the array of districts to a new view controller that can display them. The new view controller should have a property called districts, I would also recommend creating an initializer that accepts an array of districts which sets this property.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *districts = [[self.content objectAtIndex:indexPath.row] valueForKey:#"districts"];
DistrictsViewController *districtsvc =
[[DistrictsViewController alloc] initWithNibName:nil
bundle:nil
districts:districts];
[self.navigationController pushViewController:districtsvc];
}
From your example I'm not sure where the school info would come from so if it is difficult to say if you would be able to easily create a single generic view controller to drill down from state to school.

Related

How to select multiple rows from UIPickerView ios

I want to select more then one rows from UIPickerView and place in UITextField . Language used in my project is objective c . I can't understand logic
As much I have understood what you want to achieve is you want to select more than one item from a list and show it in a UITextField. For that you can do this:
At first Drag and drop a UITableView into your storyboard and connect datasource and delegates.
Drag an UITableViewCell into your tableview and give the reuserIdentifier as TableCell.
Drag an UILabel and an UIImageView onto your tableCell. Give tags to your label and imageView as 888 and 999. Make the imageview like a small box and place it just beside your label.
Take an array for showing the list you want to show user to select items and populate the UITableView with it.
Take another array in which you want to store the selected items.
Just under your implementation declare two arrays like this:
#implementation ViewController {
NSMutableArray *arrItems;
NSMutableArray *arrSelectedItems;
}
Now in your viewDidLoad populate your item array.
- (void)viewDidLoad {
[super viewDidLoad];
arrItems = [[NSMutableArray alloc]init];
for (int i = 0; i < 10; i++) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setObject:[NSString stringWithFormat:#"Item%d",i+1] forKey:#"itemName"];
[dict setObject:#"0" forKey:#"isSelected"];
[arrItems addObject:dict]
}
arrSelectedItems = [[NSMutableArray alloc]init];
}
Now implement the DataSource and Delegate methods of UITableView.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return arrItems.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TableCell"];
NSMutableDictionary *dict = [arrItems objectAtIndex:indexPath.row];
UILabel *lblItemName = (UILabel *)[cell.contentView viewWithTag:888];
lblItemName.text = [dict valueForKey:#"itemName"];
UIImageView *imageView = (UIImageView *)[cell.contentView viewWithTag:999];
if ([[dict valueForKey:#"isSelected"]isEqualToString:#"0"]) {
imageView.backgroundColor = [UIColor redColor];
} else {
imageView.backgroundColor = [UIColor greenColor];
}
return cell;
}
Now implement the multiple selection login in didSelectRowAtIndexPath like below:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableDictionary *dict = [arr objectAtIndex:indexPath.row];
if ([arrSelectedItems containsObject:dict]) {
[arrSelectedItems removeObject:dict];
} else {
[arrSelectedItems addObject:dict];
}
if ([[dict valueForKey:#"isSelected"]isEqualToString:#"0"]) {
[dict setObject:#"1" forKey:#"isSelected"];
} else {
[dict setObject:#"0" forKey:#"isSelected"];
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
//Here you can show the selected Objects in `UITextField`
self.yourTexField.text = [arrSelectedItems componentsJoinedByString:#","];
}
P.S: I have written the total code here, haven't tested it, so if any errors occurs feel free to leave a comment below, I will be there to solve it.
I think it will help you.
you can create a new tableview with each row as a language, then present it when user respond to textfield.
You can look here for some starter code on how to implement the multiple selection logic.
A textfield has a property call inputview, you can try replacing that with tableview.

Saving, retaining, & retrieving selected cells in array

I've read every post and tried every solution multiple times, but cannot get correct functionality.
I have a tableview with data from a local JSON file. I need the user to be able to:
select multiple cells
show check marks on selected cells
write those selections to an array
delete the selections when unchecked
5. save/retain the selections with the check marks when user switches view or leaves and comes back to tableview, closes and reopens app, etc.
I've managed to get 1-4 working, but I'm stuck on #5 and can't figure it out for the life of me. I've tried NSUserDefaults every way I could. Any help is appreciated. Below is the current code.
Also, why am I having to double click a cell to uncheck it?
#interface FilterViewController () <UISearchResultsUpdating>
#property (nonatomic, strong) NSArray *IngredientsArray;
#property (nonatomic, strong) UISearchController *searchController;
#property (nonatomic, strong) NSMutableArray *searchResults;
#property (nonatomic, strong) NSMutableArray *selectedCell;
#property (nonatomic, strong) NSMutableArray *selectedIngredients;
//I added this property to keep track of the selected row
#property (strong,nonatomic) NSIndexPath *selectedPath;
#end
#implementation FilterViewController {
NSArray *_locations;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.selectedIngredients = [NSMutableArray array];
self.selectedCell = [NSMutableArray array];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.lastIndexPath = [defaults objectForKey:#"lastIndexPathUsed"];
// Create a new JSONLoader with a local file URL
JSONLoaderIngreds *jsonLoader = [[JSONLoaderIngreds alloc] init];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"locations" withExtension:#"json"];
// There's no transition in our storyboard to our search results tableview or navigation controller
// so we'll have to grab it using the instantiateViewControllerWithIdentifier: method
UINavigationController *searchResultsController = [[self storyboard] instantiateViewControllerWithIdentifier:#"FilterViewSearchResultsNavController"];
// Our instance of UISearchController will use searchResults
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
// The searchcontroller's searchResultsUpdater property will contain our tableView.
self.searchController.searchResultsUpdater = self;
// create the searchBar programatically.
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x,
self.searchController.searchBar.frame.origin.y,
self.searchController.searchBar.frame.size.width, 44.0);
self.tableView.tableHeaderView = self.searchController.searchBar;
//Sets LocationsViewController as presenter for LocationDetailViewController after searxh results dsiplayed
//and selected.. Required so searchbar doesn't show in detailsview after segue, and instead, default nav
//controller back button displays.
self.definesPresentationContext = true;
// Load the data on a background queue...
// As we are using a local file it's not really necessary, but if we were connecting to an online URL then we'd need it
//NSString *ingreds = [dict objectForKey:#"ingredients"]
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_locations = [jsonLoader ingredientsFromJSONFile:url];
// Now that we have the data, reload the table data on the main UI thread
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
});
}
// Just before showing the LocationViewController, set the selected Location object
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
LocationsViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.location = [_locations objectAtIndex:indexPath.row];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewWillAppear:(BOOL)animated
{
//I added this if clause to select the row that was last selected
if (self.selectedPath != nil) {
[self.tableView selectRowAtIndexPath:self.selectedPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
#pragma mark - Table View Controller Methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedPath = indexPath;
NSString *Ingredient = [_locations objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if([self isRowSelectedOnTableView:tableView atIndexPath:indexPath]){
[self.selectedCell removeObject:indexPath];
[self.selectedIngredients removeObject:Ingredient];
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
[self.selectedCell addObject:indexPath];
[self.selectedIngredients addObject:Ingredient];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
NSLog(#"***************Selected Ingredients**************** %#", self.selectedIngredients);
NSUserDefaults *userdefaults = [NSUserDefaults standardUserDefaults];
[userdefaults setObject:[NSString stringWithFormat:#"%ld",(long)indexPath.section] forKey:#"lastIndexPathUsed"];
[userdefaults synchronize];
}
-(BOOL)isRowSelectedOnTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath
{
return ([self.selectedCell containsObject:indexPath]) ? YES : NO;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *unifiedID = #"FilterCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:unifiedID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:unifiedID];
}
cell.textLabel.text = [_locations objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:#"ingredientsicon3232.png"];
//if the indexPath was found among the selected ones, set the checkmark on the cell
cell.accessoryType = ([self isRowSelectedOnTableView:tableView atIndexPath:indexPath]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_locations count];
}
UPDATE:
I managed to changed code as suggested to save selections to array in NSUserDefaults using the updated code below, but I still can't figure out the cellForRowAtIndexPath code needed to save/recall checkmarks.
How would I code cellForRowAtIndexPath to recall checkmarks?
Saving selections to array with this code:
ViewDidLoad code:
_selections = [NSMutableArray arrayWithArray:(NSArray *)[[NSUserDefaults standardUserDefaults] objectForKey:#"selections"]];
if(_selections == nil){
_selections = [[NSMutableArray alloc] init];
}
didSelectRowAtIndexPath code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
{
if ([_selections containsObject: cell.textLabel.text] == NO){
[_selections addObject:cell.textLabel.text];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
[_selections removeObject:cell.textLabel.text];
cell.accessoryType = UITableViewCellAccessoryNone;
}
NSLog(#"***************Selected Ingredients**************** %#", _selections);
NSUserDefaults *userdefaults = [NSUserDefaults standardUserDefaults];
[userdefaults setObject:_selections forKey:#"selections"];
[userdefaults synchronize];
NSLog(#"-------------NSUserDefaults------------%#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation])
}
Your solution is pretty straight forward.
First thing is you need to save an array of selected index paths to your user default object instead of just last selected path.
Whenever a user selects or deselects a row. Add and remove objects from the same array and save it back to your user defaults.
In your cellForRowAtIndexPath, check whether your index path exists in the array saved in the user defaults. If it exists, select the row with checkmark otherwise leave it as it is.
Hope this helps.
I think instead of working with those indexPath, i recommended you to work straight with the data itself, add a Bool property to indicate the selection in your Ingredient class, then save the whole array in CoreData/Realm/NSUserDefault, that way is more correctly since your data can be change, lead to the selection indexPath that you save can be not correct anymore

How to populate an array from detailViewController and return to masterViewController in iOS?

I have a tableView from which I navigate to a 'viewController' ie PassengerInfoViewController in which I have a form.I fill up the form , make a dictionary out of it and add it to a NSMutableArray.
So when I am going back to the tableView' I would like to pass this array and then reload thetableView` with the filled array.
So here's what I am doing : -
//navigate to the form
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
PassengerInfoViewController *pivc = [self.storyboard instantiateViewControllerWithIdentifier:#"PassengerInfoViewController"];
[self.navigationController pushViewController:pivc animated:YES];
}
//After filling up the form
-(void)goBackToPassengerDetails:(UIButton*)sender{
NSString *title = self.titleTextField.text;
NSString *fname = self.firstNameTextField.text;
NSString *lname =self.lastNameTextField.text;
NSString *ageStr = self.ageTextField.text;
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setValue:title forKey:#"title"];
[dict setValue:fname forKey:#"firstName"];
[dict setValue:lname forKey:#"lastName"];
[dict setValue:ageStr forKey:#"age"];
Passenger *passengerObj = [Passenger sharedInstance]; //Singleton
[passengerObj.passengerDetailArray addObject:dict];
PassengerDetailsViewController *pdc = [self.storyboard instantiateViewControllerWithIdentifier:#"PassengerDetailsViewController"];
[pdc getPassengerinfo:passengerObj.passengerDetailArray];
[self.navigationController popViewControllerAnimated:YES];
Once I navigate back I reload the table view.However celForRow method doesn't populate new values.
-(void)getPassengerinfo:(NSMutableArray*)arr{
passenger_infoArray =[[NSMutableArray alloc]init];
passenger_infoArray = arr;
NSLog(#"Passenger Info Array : %#", passenger_infoArray);//Shows array of dictionaries
[passengerInfoTableView reloadData];
}
My cellForRow method looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Not called when I am doing reload data
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier ];
}
NSLog(#"Table view array : %#",passenger_infoArray);
if (passenger_infoArray.count>0) {
NSString *temp= [[passenger_infoArray objectAtIndex:indexPath.row]objectForKey:#"firstName"];
cell.textLabel.text = temp;
}else{
cell.textLabel.text = [NSString stringWithFormat:#"Passenger %ld", (long)indexPath.row+1];
cell.imageView.image = [UIImage imageNamed:#"user.png"];
}
return cell;
}
You can pass data using
Delegates
Notifications
Properties
Global variables
Using Delegates is good practice, and last two are not preferred.
You can pass data to previous view controller by using delegate method
In PassengerInfoViewController.h add below code just above of your import statement
#protocol passengerInfoViewControllerdelegate <NSObject>
-(void)sendData:(NSMutableArray *)arrData;
#end
After this create one property, as shown below
#property(nonatomic,assign)id delegate;
Now, in PassengerInfoViewController.m synthesize the property and after that
-(void)viewWillDisappear:(BOOL)animated
{
[delegate sendData:yourArray];
}
Now in tableview
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
PassengerInfoViewController *pivc = [self.storyboard instantiateViewControllerWithIdentifier:#"PassengerInfoViewController"];
pivc.delegate = self; //Add this line to your code
[self.navigationController pushViewController:pivc animated:YES];
}
Now, it's time to implement delegate method which we created in viewcontroller
-(void)sendData:(NSMutableArray *)arrData
{
passenger_infoArray = arrData;
[passengerInfoTableView reloadData];
}
Old way to do this was using delegates. Better way to do this is via an unwind segue because there's much less code and you don't have to create a protocol.
Once your form completes, your PDC should call [self performSegue:#"theNameOfTheUnwindSegueeYouCreateInTheStoryboard"]. Then, in your PDC's prepareForSegue method, get the destinationViewController property from the storyboardSegue parameter - this is your VC containing the Table View. Set whatever model property you're using to back the table view - in your case it looks to be the passenger_infoArray object, which you'll have to make mutable if isn't already and also expose publicly if it isn't already.

trying to get a 'Search Bar and Display Controller' to repopulate a table view

I am trying to get the 'Search Bar and Display Controller' functionality to work in an iOS app. I am able to NSLog in repsonse to a search query and hardcode in a new array but am unable to get the table view to repopulate. I have the following for in response to a user submit on the search button:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
NSLog(#"You clicked the search bar");
NSMutableArray *filteredResults=[[NSMutableArray alloc] init];
Person *filteredPerson=[[Person alloc] init];
filteredPerson.firstName=#"Josie - here i am";
[filteredResults addObject:filteredPerson];
_objects=filteredResults;
self.tableView.dataSource = _objects;
[self.tableView reloadData];
}
Any ideas on making this repopulate would be appreciated.
thx
edit #1
It looks like this is populating the _objects NSMutableArray:
- (void)insertNewObject:(id)sender
{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
Should I just create a new _objects and use insertNewObject rather than the addObject code I have above? Would this bypass the need to deal with the dataSource property of the table view?
edit 2
per #ian
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
/*
NSDate *object = [_objects objectAtIndex:indexPath.row];
cell.textLabel.text = [object description];
*/
Person *rowPerson = [_objects objectAtIndex:indexPath.row];
cell.textLabel.text = [rowPerson firstName];
return cell;
}
thx
I have a UITableView that uses an NSMutableArray to hold the data. Here's how it works: set the UISearchDisplayController's delegate to your TableView controller, and when the UITableViewDelegate methods are called (numberOfRows, numberOfSections, cellForRowAtIndexPath, etc) you can do the following to serve up the search data when appropriate:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger numberOfRows = 0;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
//This is your search table --- use the filteredListContent (this is the NSMutableArray)
numberOfRows = [filteredListContent count];
}
else
{
//Serve up data for your regular UITableView here.
}
return numberOfRows;
}
You should take a look at the UISearchDisplayDelegate documentation. You can use these methods to update your filteredListContent array, as follows:
#pragma mark -
#pragma mark Content Filtering
- (void)filterContentForSearchText:(NSString*)searchText
{
//In this method, you'll want to update your filteredListContent array using the string of text that the user has typed in. For example, you could do something like this (it all depends on how you're storing and retrieving the data):
NSPredicate *notePredicate = [NSPredicate predicateWithFormat:#"text contains[cd] %#", searchText];
self.filteredListContent = [[mainDataArray filteredArrayUsingPredicate:notePredicate] mutableCopy];
}
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
The line self.tableView.dataSource = _objects; is setting your array as the datasource for the UITableView. I assume that you don't have an NSArray subclass that implements the UITableViewDataSource protocol?
Try removing that line, and letting your existing datasource handler deal with the change in data.

Changing cell content in UITableView when tapped

I'm building a app which has a table view. But I want this to be a table view which expands the cell when you tap on it and close when you tap a second time.
But I was wondering if the following is possible. When the cell isn't selected you only see a picture, title and the beginning of the text. But as soon as the cell is selected, it will expand and show even more subviews, i.e. image views.
Is this possible? For instance, to hide a subview in the cell, and as soon a it is tapped it's visible and aligned the right way? And of course, how do i do that?
Thnx!!!
I did something similar quite a few time ago. You'll find the code at github.
Note that it is very rough, as it where my beginning iPhone days, i.e. properties are missing.
.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
NSIndexPath *selectedIndexPath;
NSDictionary *articles;
}
#end
.m
#import "FirstViewController.h"
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
selectedIndexPath = nil;
articles = [[NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:#"one", #"two", #"three",
#"four", #"five", #"six",
#"seven", #"eight", #"nine",
#"ten", #"eleven", nil]
forKey:#"title"] retain];
}
- (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.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[selectedIndexPath release];
[articles release];
[super dealloc];
}
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[articles allKeys] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[articles allKeys] objectAtIndex : section];
}
- (int)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
id key = [[articles allKeys] objectAtIndex:section];
return [[articles objectForKey : key] count];
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ((selectedIndexPath != nil) && (selectedIndexPath.row == indexPath.row))
return 80.0;
return 40.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * MyIdentifier = #"MyIdentifier";
UITableViewCell * cell = [self.tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
id key = [[articles allKeys] objectAtIndex:indexPath.section];
cell.textLabel.text = [[articles objectForKey:key] objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedIndexPath == indexPath) {
selectedIndexPath = nil;
} else {
selectedIndexPath = indexPath;
}
[self.tableView deselectRowAtIndexPath : indexPath animated : NO];
[tableView beginUpdates];
[tableView endUpdates];
}
#end
Yes, I do this in the app I'm working on now.
You need to keep track of the state that a cell is in, either open or closed. If only 1 cell can be open at a time, you can do this by just keeping a reference to the current indexPath. If multiple cells can be open at the same time, you'll need an array of booleans, which tracks if each one is open or closed.
In heightForRowAtIndexPath, just return the correct height based on if the row is open or closed.
In cellForRowAtIndexPath, if the row is closed, hide all the content that shouldn't be visible when it's closed. The views can still be there, but they should be set to hidden = YES.
Finally, in didSelectRowAtIndexPath, set the given index path to open if it was closed, and closed if it was open, then reload the cell with [tableView reloadRowsAtIndexPaths:]. If you are only allowing 1 at a time to be open, then just set your current open index path to the one that was selected, and reload both the one that was selected as well as the one that had been previously open.

Resources