XCode 4.2 NSArray database for tableview - iphone

I'm a beginner i have some problem with NSArray.
I created an array and add an new object(dataclass) to the locationArray
dataclass.m
#synthesize UniqueID,name, address, latitude, longitude, type;
+ (id)LocationWithID:(NSString *)UniqueID name:(NSString *)name address:(NSString *)address latitude:(NSString *)latitude longitude:(NSString *)longitude type:(NSString *)type
{
agilentLocation *newLocation = [[self alloc] init];
newLocation.UniqueID = UniqueID;
newLocation.name = name;
newLocation.address = address;
newLocation.latitude = latitude;
newLocation.longitude = longitude;
newLocation.type = type;
return newLocation;
}
Table.m
NSMutableArray *LocationArray;
[LocationArray addObject:[dataclass LocationWithID:UniqueID name:name address:address latitude:latitude longitude:longitude type:type]];
I would like to ask how could I call the value under my dataclass. For example I need to call the name under the dataclass for my table title.
Normally when i'm building a table i'll use
cell.textLabel.text = [LocationArray objectAtIndex:indexpath.row];
but in this case there is only one object which is "dataclass" but it has several objects under it.
how should i display the name on the table.
The code below is used to print out values for the object(dataclass)
for (dataclass *theLocation in LocationArray) {
NSLog(#"Name: %# Address: %# Latitude: %g Longitude: %g Type :%#", theLocation.name, theLocation.address ,[theLocation.latitude doubleValue], [theLocation.longitude doubleValue], theLocation.type);
}
Screenshot

// First Return The Number of Sections.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)iTableView {
NSInteger aSectionCount = 0;
return aSectionCount;
}
// Now return the No Of Rows. In your case the total count of DataClass in LocationArray
- (NSInteger)tableView:(UITableView *)iTableView numberOfRowsInSection:(NSInteger)iSection {
NSInteger aRowCount = 0;
aRowCount = [LocationArray count];
return aRowCount;
}
// Now for the respective Row, Pick the DataClass from Location Array and display the name.
- (UITableViewCell *)tableView:(UITableView *)iTableView cellForRowAtIndexPath:(NSIndexPath *)iIndexPath {
NSInteger theRow = iIndexPath.row;
NSInteger theSection = iIndexPath.section;
NSString *aCellIdentifier = [NSString stringWithFormat:#"Cell-%d-%d-%d", theSection, theRow, iTableView.tag];
UITableViewCell *aCell = (UITableViewCell *)[iTableView dequeueReusableCellWithIdentifier:aCellIdentifier];
if (aCell == nil) {
aCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:aCellIdentifier] autorelease];
}
dataclass *theLocation = [LocationArray objectAtIndex:theRow];
aCell.TitleLabel.Text = theLocation.name;
return aCell;
}
Please Note that the LocationArray should be a class variable. Public or Private based on your convenience.
Hope this is what you are looking for...

Related

How to calculate the depth of the custom object with subObjects of the same type?

I have an object call CEKnot and it has property called subknots which is an array of similar CEKnot objects. Now I want to calculate how deep this tree goes.. This is not a binary tree as the subknots can be more than two. here is the code I have tried..
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CEProjectDTO* project = [self.tblViewDatasource objectAtIndex:indexPath.row];
CEServiceSession *serviceSession = [CEServiceSession new];
[CEProgressHUD showWithTitle:#"Loading..."];
[serviceSession.projectPhaseService getProjectPhasesWithProjectID:project.i3D onFinish:^(NSArray * phaseList){
self.phaseDTOList = phaseList;
NSLog(#"Count is %d",[self.phaseDTOList count]);
[CEProgressHUD hide];
}onError:^(NSString *title, NSString *message) {
[CEProgressHUD hide];
[CEAlertViewHelper showOKAlertViewWithTitle:title message:message];
}];
self.mindMapLevelObjectCount = nil;
self.mindMapLevelObjectCount = [[NSMutableDictionary alloc] init];
self.level = 1;
self.numberOfKnotsOnThisLevel = 0;
CEProjectToKnotConverter* converter = [CEProjectToKnotConverter new];
CEProjectKnot* baseKnot = [converter convertProject:project withPhases:self.phaseDTOList];
self.numberOfKnotsOnThisLevel = self.numberOfKnotsOnThisLevel + [baseKnot.subKnots count];
[self.mindMapLevelObjectCount setObject:[NSNumber numberWithInt:self.numberOfKnotsOnThisLevel] forKey:[NSString stringWithFormat:#"%d",self.level]];
self.numberOfKnotsOnThisLevel = 0;
self.level = 2;
[self calculateMindMapLevels:baseKnot];
NSLog(#"MIND MAP LEVEL OBJE %#", self.mindMapLevelObjectCount);
CEMindMapViewController *mindMapViewController = [[CEViewControllerManager sharedInstance] createViewController:[CEMindMapViewController class]];
mindMapViewController.baseKnot = baseKnot;
[self presentModalViewController:mindMapViewController animated:YES];
}
-(void)calculateMindMapLevels:(CEKnot*)projectKnot{
for (CEKnot* subKnot in projectKnot.subKnots) {
self.numberOfKnotsOnThisLevel = self.numberOfKnotsOnThisLevel + [subKnot.subKnots count];
NSLog(#"Level %d total of nots %d project not subknot %d", self.level,self.numberOfKnotsOnThisLevel, [subKnot.subKnots count]);
}
[self.mindMapLevelObjectCount setObject:[NSNumber numberWithInt:self.numberOfKnotsOnThisLevel] forKey:[NSString stringWithFormat:#"%d",self.level]];
for (CEKnot* subKnot in projectKnot.subKnots) {
[self calculateMindMapLevels:subKnot];
self.numberOfKnotsOnThisLevel = 0;
}
self.level = _level +1;
}
I am not getting the depth correctly and have tried numerous algorithm but cant get the depth correct. Any help is appreciated.
As Long as your tree does not store its depth you have to get the Level of each knot and return the highest.
A recursive -untested- approach
int numberOfLevels(CEKnot *knot, int parentLevel) {
int ownLevel = parentLevel+1;
int highestLevel = ownLevel;
for (CEKnot* subKnot in knot) {
int subLevel = numberOfLevels(subKnot, ownLevel);
highestLevel = MAX(highestLevel, subLevel);
}
return highestLevel;
}

tableview will not update on phone

If this is a repost, I apologize, but I have been scouring the net and cant seem to find anything that works. I have a list of workouts that I display in a tableview that are gathered in plists in the bundle. There is a also a separate tab that I have that allows a user to build their own workouts and save them in the documents folder plist file. Once they are saved, they are added to the table view. In the simulator, everyuhting works fine. But on the actual device, it is not updated unless I close the program for an extended period of time, reload the program from xcode, or turn the phone off. I have tried adding [[self tableview] reload] to "viewDidLoad", "viewWillappear", and "viewDidAppear" and none of them work. Once again, the file is saved, the updating does work in the simulator, and it doesn't update in the phone right away. Any suggestions? Thanks.
Edit: i know it is a long piece of code, but should be straight forward (hopefully lol)
#import "BIDWODList.h"
#import "BIDWODDetails.h"
#define kFileName #"SavedDFWorkouts.plist"
#interface BIDWODList ()
#end
#implementation BIDWODList
#synthesize names;
#synthesize savedNames;
#synthesize keys;
#synthesize details;
#synthesize wodType;
#synthesize benchmarkGirls;
#synthesize theNewGirls;
#synthesize heroes;
#synthesize savedDFGWorkouts;
#synthesize chosenWOD;
#synthesize chosenDetails;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *buildBenchmarkGirls = [[NSMutableArray alloc] init];
NSMutableArray *buildTheNewGirls = [[NSMutableArray alloc] init];
NSMutableArray *buildHeroes = [[NSMutableArray alloc] init];
NSBundle *bundle = [NSBundle mainBundle];
NSURL *plistURL = [bundle URLForResource:#"CrossfitWOD" withExtension:#"plist"];
//put the contents of the plist into a NSDictionary, and then into names instance variable
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:plistURL];
self.names = dictionary;
//take all the keys in the dictionary and make an array out of those key names
self.keys = [self.names allKeys];
for (NSString *nameCheck in keys){
self.details = [names valueForKey:nameCheck];
if ([[self.details valueForKey:#"Type"] isEqualToString:#"The Benchmark Girls"]) {
[buildBenchmarkGirls addObject:nameCheck];
}else if ([[self.details valueForKey:#"Type"] isEqualToString:#"The New Girls"]) {
[buildTheNewGirls addObject:nameCheck];
}else {
[buildHeroes addObject:nameCheck];
}
}
NSString *filePath = [self dataFilePath];
NSMutableDictionary *savedWorkout = [[NSMutableDictionary alloc]initWithContentsOfFile:filePath];
self.savedNames = savedWorkout;
self.savedDFGWorkouts = [[savedWorkout allKeys] sortedArrayUsingSelector:#selector(compare:)];
self.benchmarkGirls = [buildBenchmarkGirls sortedArrayUsingSelector:#selector(compare:)];
self.theNewGirls = [buildTheNewGirls sortedArrayUsingSelector:#selector(compare:)];
self.heroes = [buildHeroes sortedArrayUsingSelector:#selector(compare:)];
//[[self tableView] reloadData]; //reloads the data in case a DFG workout was saved
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.names = nil;
self.keys = nil;
self.benchmarkGirls = nil;
self.theNewGirls = nil;;
self.heroes = nil;
self.details = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSString *)dataFilePath {
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFileName];
}
-(void)viewDidAppear:(BOOL)animated{
[[self tableView] reloadData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 4;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0) {
return [benchmarkGirls count];
}else if (section == 1){
return [theNewGirls count];
}else if (section == 2){
return [heroes count];
}else{
return [savedDFGWorkouts count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SectionsTableIdentifier];
}
if (section == 0) {
cell.textLabel.text = [benchmarkGirls objectAtIndex:row];
}else if (section == 1) {
cell.textLabel.text = [theNewGirls objectAtIndex:row];
}else if (section == 2) {
cell.textLabel.text = [heroes objectAtIndex:row];
}else{
cell.textLabel.text = [savedDFGWorkouts objectAtIndex:row];
}
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section == 0) {
return #" The Benchmark Girls";
}else if (section == 1){
return #"The New Girls";
}else if (section ==2){
return #"The Heroes";
}else{
return #"Saved DFG Workouts";
}
}
#pragma mark - Table view delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
BIDWODDetails *destination = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
if (section == 0) {
self.chosenWOD = [self.benchmarkGirls objectAtIndex:row];
self.chosenDetails = [names objectForKey:chosenWOD];
}else if (section == 1) {
self.chosenWOD = [self.theNewGirls objectAtIndex:row];
self.chosenDetails = [names objectForKey:chosenWOD];
}else if (section ==2) {
self.chosenWOD = [self.heroes objectAtIndex:row];
self.chosenDetails = [names objectForKey:chosenWOD];
}else {
self.chosenWOD = [self.savedDFGWorkouts objectAtIndex:row];
self.chosenDetails = [savedNames objectForKey:chosenWOD];
}//end if
//self.chosenDetails = [names objectForKey:chosenWOD];
//[destination setValue:chosenWOD forKey:#"chosenWOD"];
//[destination setValue:chosenDetails forKey:#"chosenDetails"];
destination.chosenWOD = self.chosenWOD;
destination.chosenDetails = self.chosenDetails;
}
#end
Different behaviour between the simulator and the device is often related to incorrect case being used in filenames - the simulator isn't case sensitive, and the device is. Check that you have the correct case used everywhere you reference the plist file.
Alternatively, on the simulator you are able to write directly to the application bundle, but on the device this is not possible and you can only write to certain directories in your application's sandbox, typically the documents directory. You would normally copy a plist to the documents directory on first run, and use that file thereafter.
If I understand right your code you load plist file only in viewDidLoad, but most likely this function called only when you first time load your view. To make it work you should load plist
in viewDidAppear. Something like this:
- (void)viewDidAppear {
NSBundle *bundle = [NSBundle mainBundle];
NSURL *plistURL = [bundle URLForResource:#"CrossfitWOD" withExtension:#"plist"];
//put the contents of the plist into a NSDictionary, and then into names instance variable
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:plistURL];
self.names = dictionary;
//take all the keys in the dictionary and make an array out of those key names
self.keys = [self.names allKeys];
for (NSString *nameCheck in keys){
self.details = [names valueForKey:nameCheck];
if ([[self.details valueForKey:#"Type"] isEqualToString:#"The Benchmark Girls"]) {
[buildBenchmarkGirls addObject:nameCheck];
}else if ([[self.details valueForKey:#"Type"] isEqualToString:#"The New Girls"]) {
[buildTheNewGirls addObject:nameCheck];
}else {
[buildHeroes addObject:nameCheck];
}
}
NSString *filePath = [self dataFilePath];
NSMutableDictionary *savedWorkout = [[NSMutableDictionary alloc]initWithContentsOfFile:filePath];
self.savedNames = savedWorkout;
self.savedDFGWorkouts = [[savedWorkout allKeys] sortedArrayUsingSelector:#selector(compare:)];
[self.tableView reloadData];
}
If it works in the Simulator and does not on the phone, almost for sure the problem is a timing issue. Saving files on a real phone takes much longer than on the simulator.
You should do the following:
when you save a file, log it, and log the return code from the save. If the way you save does not provide a return code, use NSFileManager to verify the file is in fact where it should be and even the size of it. This takes time to do but you should do it.
when your table is asking for the number of this and that, log it, and lot what is returned. You may find that that this comes before the files are saved.
It takes time and effort, but if you start logging all relevant things, you can find it. I just spend 6 hours today tracking down a race condition I had thought could never happen, and it was only after looking at a huge trail of messages that I was able to see the problem.
Almost for sure you will see that either file is not saved, its not where you thought it was, or that the phone timing means some events happen later than they do in the Simulator.

how to Sort/Group JSON items

***Updated Question ******
The JSON data is the Gym Classes for all week .
At the moment I am downloading and saved the JSON data to An NSObject SearchResult then display on my UItableviewController one row per gym Class.
But I want to Group items (Gym Classes) By Day and display each day on is own table.
So my question is do I need to group items by date "DAY_OF_WEEK" and store each day in to is own array (MonArray ... SunArray)?
or collect all the data Like I am doing now and sort the array in to groups for each day?
I hope this make sense.
thanks for your help .
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: JsonURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES]; }); }
-(SearchResult *)parseTrack:(NSDictionary *)dictionary {
SearchResult *searchResult1 = [[SearchResult alloc] init];
searchResult1.day = [[dictionary objectForKey:#"post"] objectForKey:#"DAY_OF_WEEK"];
searchResult1.classType= [[dictionary objectForKey:#"post"] objectForKey:#"CLASS_TYPE"];
return searchResult1;
}
- (void)fetchedData:(NSData *)responseData { //parse out the json data
searchResults2 = [NSMutableArray arrayWithCapacity:10];
NSError* error;
NSDictionary* dictionary = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions error:&error];
NSArray *array = [dictionary objectForKey:#"posts"];
NSLog(#"array : %#",array);
if (array == nil) {
NSLog(#"Expected 'posts' array");
return;
}
for (NSDictionary *resultDict in array) {
SearchResult *searchResult3;
searchResult3 = [self parseTrack:resultDict];
if (searchResult3 != nil) {
[searchResults2 addObject:searchResult3];
}
NSLog(#"day: %#, class: %#", [[resultDict objectForKey:#"post"] objectForKey:#"DAY_OF_WEEK"], [[resultDict objectForKey:#"post"] objectForKey:#"CLASS_TYPE"]);
}
[self.tableView reloadData];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return searchResults2.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SRCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SRCell1"];
// Configure the cell...
SearchResult *searchResult1 = [searchResults2 objectAtIndex:indexPath.row];
cell.daynameLabel.text = searchResult1.day;
return cell;
}
JSON output NSlog
2012-04-26 06:12:51.256 passingdata[91699:fb03] array : (
{
post = {
"CLASS_LEVEL" = "Intro/General";
"CLASS_TYPE" = "Muay Thai";
"DAY_OF_WEEK" = Friday;
ID = 19;
"ORDER_BY" = 5;
TIME = "1:00pm - 2:30pm";
};
}
{
post = {
"CLASS_LEVEL" = "General/Intermediate/Advanced";
"CLASS_TYPE" = "Muay Thai Spar - Competitive";
"DAY_OF_WEEK" = Friday;
ID = 27;
"ORDER_BY" = 5;
TIME = "6:00pm - 9:00pm";
};
},
{
post = {
"CLASS_LEVEL" = "Fighters/Advanced/Intermediate";
"CLASS_TYPE" = "Fighters Training";
"DAY_OF_WEEK" = Monday;
ID = 1;
"ORDER_BY" = 1;
TIME = "9:30am - 11:00pm";
};
},
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"DAY_OF_WEEK" ascending:TRUE];
[sourceArr sortUsingDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
May this will help you.

How to set Grouped UITableview Section names without a NSFetchedResultsController in play

I have used a number of grouped tables tied to core data managed objects, where the sectionNameKeyPath value is used to identify the attribute in the data that should be used to denote sections for the table.
But how do I indicate the "sectionNameKeyPath equivalent" when I have a table that is being use to present an NSMutableArray full of objects that look like this:
#interface SimGrade : NSObject {
NSNumber * scoreValue;
NSString * commentInfo;
NSString * catName;
}
I would like to have sections defined according to the "catName" member of the class.
Consider, for example that my mutablearray has 5 entries where the 5 "catName" values are "Blue", "Blue", "Blue", "Red", and "Red". So I'd want the number of sections in the table for that example to be 2.
So, what I would 'like to do' could be represented by the following pseudo-code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return (The number of unique catNames);
}
Note: My interest in doing this is not so much for displaying separate sections in the table, but rather so that I can more easily calculate the sums of scoreValues for each category.
<<<<< UPDATE >>>>>>>>>
Joshua's help, as documented in his response has been right on. Here are the two new handlers for number of sections and number of rows per section...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSMutableSet *counter = [[NSMutableSet alloc] init];
[tableOfSimGrades enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[counter addObject:[object catName]];
}];
NSInteger cnt = [counter count];
[counter release];
NSLog(#">>>>> number of sections is -> %d", cnt);
return cnt;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSMutableDictionary *counter = [[NSMutableDictionary alloc] init];
NSMutableArray *cats = [[NSMutableArray alloc] init];
__block NSNumber *countOfElements;
[tableOfSimGrades enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// check the dictionary for they key, if it's not there we get nil
countOfElements = [counter objectForKey:[object catName]];
if (countOfElements) {
// NSNumbers can't do math, so we use ints.
int curcount = [countOfElements intValue];
curcount++;
[counter setObject:[NSNumber numberWithInt:curcount] forKey:[object catName]];
NSLog(#">>>> adding object %d to dict for cat: %#", curcount, [object catName]);
} else {
[counter setObject:[NSNumber numberWithInt:1] forKey:[object catName]];
[cats addObject:[object catName]];
NSLog(#">>>>> adding initial object to dict for cat: %#", [object catName]);
}
}];
countOfElements = [counter objectForKey:[cats objectAtIndex: section]];
int catcount = [countOfElements intValue];
[counter release];
[cats release];
return catcount;
}
My current issue with this routine now lies in the following function... It is ignorant of any sections in the nsmutableArray and so for each section, it starts at index 0 of the array instead of at the 0th element of the appropriate section.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
SimGrade *tmpGrade = [[SimGrade alloc] init];
tmpGrade = [tableOfSimGrades objectAtIndex: indexPath.row];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Category: %#", tmpGrade.catName];
// [tmpGrade release];
return cell;
}
How do I transform the "indexpath" sent to this routine into the appropriate section of the mutableArray?
Thanks,
Phil
You could do something like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSMutableSet *counter = [[NSMutableSet alloc] init];
[arrayOfSims enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[counter addObject:object.catName];
}];
NSInteger cnt = [counter count];
[counter release];
return cnt;
}
you'd probably want to memoize that, for performance reasons (but only after profiling it).
--- EDIT ---
You can use an NSMutableDictionary, too, to get counts of individual categories.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSMutableDictionary *counter = [[NSMutableDictionary alloc] init];
__block NSNumber *countOfElements;
[arrayOfSims enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// check the dictionary for they key, if it's not there we get nil
countOfElements = [counter objectForKey:object.catName];
if (countOfElements) {
// NSNumbers can't do math, so we use ints.
int curcount = [countOfElements intValue];
curcount++;
[counter setObject:[NSNumber numberWithInt:curcount] forKey:object.catName];
} else {
[counter setObject:[NSNumber numberWithInt:1] forKey:object.catName];
}
}];
NSInteger cnt = [counter count];
// we can also get information about each category name, if we choose
[counter release];
return cnt;
}

Property changes but I can't figure out who's doing it

I have a UIViewController (called AdjustViewController) that presents another UIViewController (called SourcePickerViewController) with a UIPickerView modally. I generate instances of the AdjustViewController and they in turn make a SourcePickerViewController. I make an NSDictionary and assign it and an integer to the AdjustViewController and it in turn sets the same properties in the SourcePickerController. This way I can reuse the controllers. The NSDictionary get set up in a UITableViewController that has all the AdjustViewControllers in it.
The problem comes when some of the pickers should have 1 component and some should have 2. The integer that I pass along is called numberOfComponents When I make a picker with numberOfComponents = 1 somehow it's changing to = 2 but I can't see how. I have NSLogs all over the place and I can see it happen as soon as the picker delegate method numberOfComponentsInPickerView is called. It's 1 right before and 2 right after.
There's obviously more code, but I think I have all the important parts. Although if that were true, maybe I'd know where the problem is!
Inside MenuViewController.m
- (void)viewDidLoad {
NSLog(#"ChemicalViewController launched");
self.title = #"Adjust Chemicals";
NSMutableArray *array = [[NSMutableArray alloc] init];
// Chlorine Controller
AdjustViewController *chlorineAdjustViewController = [[AdjustViewController alloc] initWithNibName:#"AdjustViewController" bundle:nil];
chlorineAdjustViewController.title = #"FC - Free Chlorine";
chlorineAdjustViewController.numberOfComponents = 2;
NSLog(#"Generating chlorine source dictionary");
NSDictionary *chlorineSourceDictionary = [self generateChlorineDictionary];
chlorineAdjustViewController.dictionaryOfSources = chlorineSourceDictionary;
[chlorineSourceDictionary release];
[array addObject:chlorineAdjustViewController];
[chlorineAdjustViewController release];
// CYA Controller
AdjustViewController *cyaAdjustViewController = [[AdjustViewController alloc] initWithNibName:#"AdjustViewController" bundle:nil];
cyaAdjustViewController.title = #"CYA - Cyanuric Acid";
cyaAdjustViewController.numberOfComponents = 1;
NSLog(#"Generating cya source dictionary");
NSDictionary *cyaSourceDictionary = [self generateCYADictionary];
cyaAdjustViewController.dictionaryOfSources = cyaSourceDictionary;
[cyaSourceDictionary release];
[array addObject:cyaAdjustViewController];
[cyaAdjustViewController release];
Inside AdjustViewController.m
// Present the picker for chlorine selection
- (IBAction)getChemicalSource {
SourcePickerViewController *sourcePickerViewController = [[SourcePickerViewController alloc] init];
sourcePickerViewController.delegate = self;
NSLog(#"getChemicalSource setting numberOfComponents %d", self.numberOfComponents);
sourcePickerViewController.numberOfComponents = self.numberOfComponents;
NSLog(#"getChemicalSource sending numberOfComponents %d", sourcePickerViewController.numberOfComponents);
sourcePickerViewController.dictionaryOfSources = self.dictionaryOfSources;
[self presentModalViewController:sourcePickerViewController animated:YES];
[sourcePickerViewController release];
}
#pragma mark -
#pragma mark Picker View Delegate Methods
// Returns the values from the picker if a source was chosen
- (void)sourcePickerViewController:(SourcePickerViewController *)controller
didSelectSource:(NSString *)source
andConcentration:(NSString *)concentration
andConstant:(float)constant
andIsLiquid:(BOOL)isLiquid {
sourceField.text = [[NSString alloc] initWithFormat:#"%#, %#", source, concentration];
[self updateAdvice];
NSLog(#"Returned source = %#, concentration = %#, sourceConstant = %1.7f, isLiquid = %d", source, concentration, constant, isLiquid);
[self dismissModalViewControllerAnimated:YES];
}
// Returns from the picker without choosing a new source
- (void)sourcePickerViewController:(SourcePickerViewController *)controller
didSelectCancel:(BOOL)didCancel {
[self updateAdvice];
NSLog(#"Returned without selecting source");
[self dismissModalViewControllerAnimated:YES];
}
Inside SourceViewController.m
- (void)viewDidLoad {
NSLog(#"SourcePickerViewController launched");
NSLog(#"viewDidLoad");
NSLog(#"Received numberOfComponents %d", self.numberOfComponents);
self.chemicalSources = dictionaryOfSources;
NSArray *components = [self.chemicalSources allKeys];
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
self.sources = sorted; // This array has the chemical sources
if (self.numberOfComponents = 2) {
NSString *selectedSource = [self.sources objectAtIndex:0];
NSArray *chemArray = [self.chemicalSources objectForKey:selectedSource];
NSMutableArray *concentrationArray = [[NSMutableArray alloc] init];
int num = [chemArray count];
for (int i=0; i<num; i++) {
[concentrationArray addObject:[[chemArray objectAtIndex:i] chemConcentration]];
}
self.concentrations = concentrationArray;
}
[super viewDidLoad];
}
#pragma mark -
#pragma mark Picker Data Source Methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
NSLog(#"numberOfComponentsInPickerView, self.numberOfComponents = %d", self.numberOfComponents);
return self.numberOfComponents;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
NSLog(#"numberOfRowsInComponent, self.numberOfComponents = %d", self.numberOfComponents);
if (component == kSourceComponent)
return [self.sources count];
return [self.concentrations count];
}
#pragma mark Picker Delegate Methods
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == kSourceComponent)
return [self.sources objectAtIndex:row];
return [self.concentrations objectAtIndex:row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSLog(#"didSelectRow, self.numberOfComponents = %d", self.numberOfComponents);
if (numberOfComponents = 2) {
if (component == kSourceComponent) {
NSString *selectedSource = [self.sources objectAtIndex:row];
NSArray *chemArray = [self.chemicalSources objectForKey:selectedSource];
NSMutableArray *concentrationArray = [[NSMutableArray alloc] init];
int num = [chemArray count];
for (int i=0; i<num; i++) {
[concentrationArray addObject:[[chemArray objectAtIndex:i] chemConcentration]];
}
self.concentrations = concentrationArray;
[picker selectRow:0 inComponent:kConcentrationComponent animated:YES];
[picker reloadComponent:kConcentrationComponent];
}
}
}
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
if (component == kConcentrationComponent)
return 90;
return 205;
}
I didn't look through all of your code; Instead, I'd recommend writing out the properties for numberOfComponents instead of #synthesize'ing them. Just get rid of your #synthesize, and make:
- (int)numberOfComponents {
return m_numberOfComponents;
}
and
- (void)setNumberOfComponents(int aNumberOfComponents) {
m_numberOfComponents = aNumberOfComponents;
}
Then, set a breakpoint in your setNumberOfComponents function, and you should be able to see whenever it's getting called, so you can see what is going on. I hope that helps!