UItableView referencing sections from core data: - sql

I am saving some data the user inputs into a data file using SQLITE, I am saving date, time, reading, and a note, as the following:
//Create a Reading Object
Readings *readingObj = [[Readings alloc] initWithPrimaryKey:0];
readingObj.date = date.text;
readingObj.time = time.text;
readingObj.reading = reading.text;
readingObj.note = note.text;
//Add the object //This is where I add the inputs to the SQL data file
[appDelegate addReading:readingObj];
My problem or question is: I want to sort the data in a uitableview with date as the section header and other inputs under the section in a cell. The dates represent the number of sections I would have.
This is what I have for now:
- (NSInteger)numberOfSectionsInTableView:(UITableVie w * )tableView {
return 1;
}
- (NSInteger)tableView:(UITableView * )tableView numberOfRowsInSection:(NSInteger)section {
return [appDelegate.readingsArray count];
}
- (UITableViewCell * )tableView:(UITableView * )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
HistoryCell *cell = (HistoryCell * )[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[HistoryCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
//Get the object from the array.
Readings *readingObj = [appDelegate.readingsArray objectAtIndex:indexPath.row];
[cell setTodo:readingObj];
return cell;
}
readingsArray is the array where I load all the data to; it has all the data:
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Readings *readingObj = [[Readings alloc] initWithPrimaryKey:primaryKey];
readingObj.date = [NSString stringWithUTF8String:(char * )sqlite3_column_text(selectstmt, 1)];
////change to int
readingObj.time = [NSString stringWithUTF8String:(char * )sqlite3_column_text(selectstmt, 2)];
////
readingObj.reading = [NSString stringWithUTF8String:(char * )sqlite3_column_text(selectstmt, 3)];
////
readingObj.note = [NSString stringWithUTF8String:(char * )sqlite3_column_text(selectstmt, 4)];
[appDelegate.readingsArray addObject:readingObj];
[readingObj release];
How can I have multiple sections based on the dates, when I only have one array, which readingsArray that has all the data. couldn't make a dates array because it was confusing for me. The dates sections would have multiple readings during that day.
Any suggestions or thoughts? I can provide more code or explanation about my code if needed.

In the title you mentioned once core data. So you probably have a NSFetchedResultsController in your delegate that fetches all the appropriate objects from core data! Then you should set a NSFetchedResultsController property in your class and set it to the delegates fetchedresultscontroller, so you have can use the fetchedresultscontroller delegate methods very comfortable. Or you just initialize another fetchedresultscontroller. Why do you set the fetchedresultsc. not directly in your tableviewcontroller class?

Related

how to instantiate a CD entity without being fetched

I have a program that fetches from Core Data and then within the program if something is fetches it displays the results in my UITableView.
Now my problem is that sometimes I need the user to add another UITableCell to my UITableView but my tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section counts the elements in my array which holds my ExerciseData entities.
I've tried to create an ExerciseData entity as if it was an object but this doesnt work and crashes the program.
by doing
if ([[_ed objectAtIndex:indexPath.row] weight]) {
NSLog(#"%#",_ed);
cell.weightInput.text = [NSString stringWithFormat:#"%#",[[_ed objectAtIndex:indexPath.row] weight]];
}
it throws the error CoreData: error: Failed to call designated initializer on NSManagedObject class 'ExerciseData' that made me understand that I cant initiate a CD entity like that.
then I've tried to add to the NSArray _ed a NSString and then check for the class and if the class type was a class of NSString it shouldnt try to set cell.weightInput.text
My question are 2:
1) Is there a way to initiate an entity so that I can insert it in the array and then check if is empty so that then i can validate it on a later if statement?
if that is not possible
2) How could i populate the NSArray with something that can then be validated & escalated to multimple items but also work when there is a ExerciseData?
My goal is that the person uses - addSet to create a new UITableCell for as many times the button is pressed then i need to validate whatever is inside so that if is a ExerciseData entity and the property weight is set to populate cell.weightInput.text or otherwise dont populate it
- (void) addNewSet {
ExerciseData * newEd = [[ExerciseData alloc] init];
NSMutableArray* ar = [[NSMutableArray alloc]initWithArray:_ed];
[ar addObject:newEd];
_ed = [[NSArray alloc] initWithArray:ar];
[_mytable reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
workoutCell * cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (!cell) {
cell = [[workoutCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
}
cell.weightInput.text = #"";
if ( indexPath.row < [_ed count] ) {
if (![[[_ed objectAtIndex:indexPath.row] class] isKindOfClass:[NSString class]]) {
NSLog(#"%#",_ed);
cell.weightInput.text = [NSString stringWithFormat:#"%#",[[_ed objectAtIndex:indexPath.row] weight]];
}
}
//...
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ( [_ed count] == 0 ) {
return 1;
} else {
return [_ed count];
}
}
To create a new CoreData entity you need to:
ExerciseData *data = (ExerciseData*)[NSEntityDescription insertNewObjectForEntityForName:#"ExerciseData" inManagedObjectContext:yourContext];
Make sure to save your context after!

Checking/Unchecking a tableviewcell with sections

Previously, I had a large-ish dataset (~530 records) being displayed in a tableview. The data was held in an array of dictionaries with two keys, ID and name. Previously, I tapped a cell, it added a check mark, and it sent the cell row number to a 'selectedArray'. As I had already alphabetically sorted them (still in one section), the indexPath.row which was stored in the selectedArray corresponded to the dictionaries' array index, so I could pull data (the ID) from the record.
I decided that I would split this into headers by alphabetical order (which was an absolute pain, I don't see why it's such a complex process to insert headers into a list of records). But now, as I only used indexPath.row, when I tick the first one, it ticks the first record of each section, and only returns the number 0, so I only get the first record in the whole dataset. Is there a simple way to correct this? Really appreciate any help.
cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"inviteCell"];
// cell.accessoryType = UITableViewCellAccessoryCheckmark;
if ([checkedCells objectForKey:[NSNumber numberWithInt:indexPath.row]] != nil) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
// NSDictionary *friend = [sortedFriendsArray objectAtIndex:indexPath.row];
//---get the letter in the current section---
NSString *alphabet = [nameIndex objectAtIndex:[indexPath section]];
//---get all states beginning with the letter---
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"SELF.name beginswith[c] %#", alphabet];
NSArray *states = [sortedFriendsArray filteredArrayUsingPredicate:predicate];
if ([states count]>0) {
//---extract the relevant state from the states object---
NSDictionary *friend = [states objectAtIndex:indexPath.row];
long long fbid = [[friend objectForKey:#"id"]longLongValue];
NSString *name = [friend objectForKey:#"name"];
NSString *urlString = [NSString stringWithFormat:#"https://graph.facebook.com/%qi/picture?type=square",fbid];
NSURL *url = [NSURL URLWithString:urlString];
UILabel *eventNameLabel = (UILabel *) [cell viewWithTag:1];
eventNameLabel.text = name;
UIImageView *eventLogo = (UIImageView*) [cell viewWithTag:2];
eventLogo.image = [UIImage imageNamed:#"112-group.png"];
// eventLogo.image = [UIImage imageWithData: [NSData dataWithContentsOfURL:url]];;
}
return cell;
}
CURRENT didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSNumber *indexNumber = [NSNumber numberWithInt:indexPath.row];
NSDictionary *friend = [sortedFriendsArray objectAtIndex:indexPath.row];
long long fbid = [[friend objectForKey:#"id"]longLongValue];
NSNumber *fbidNum = [NSNumber numberWithLongLong:fbid];
if ([checkedCells objectForKey:indexNumber] != nil) {
[checkedCells removeObjectForKey:indexNumber];
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
[checkedCells setObject:fbidNum forKey:indexNumber];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
NSLog(#"Cell Pressed: %#",indexNumber);
NSLog(#"FBID: %lld",fbid);
NSLog(#"Array: %#",checkedCells);
}
It looks like you're only saving the checked cells by row, not by section and row. At minimum, don't you need to be testing the section as well as the row in this block of code?
if ([checkedCells objectForKey:[NSNumber numberWithInt:indexPath.row]] != nil) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
It's late, and I don't have my Mac handy to try this code, so maybe I'm missing something obvious. I'm not sure what you're talking about only returning zero ... is that indexPath.row?
EDIT:
To account for the section in your data array, I'd suggest storing the data as a dictionary of arrays, one dictionary entry keyed by each letter, with the inner array holding all of the entries starting with that letter. It's a little more complicated to retrieve the data, but it correctly accounts for the section.
I supposed you could create some kind of an offset to account for the number of entries in each section and then use that to index into a flat array, but that would be a lot harder to maintain, in my view. I think the dictionary of arrays is the way to go.

UITableView Load Cell and ObjectForKey

I have a problem that should be quite common. I have an Array of data called taskList this comes from a JSON and has several user data. So far, so good. I make the first objectForKey:#"desc" and returns the result (Description of user) but when I try to add another objectForKey (age for example) it shows only the age :( This is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"] autorelease];
}
NSLog(#"%#",taskList);
cell.textLabel.text = [[taskList objectAtIndex:indexPath.row] objectForKey:#"desc"];
return cell;
cell.textLabel.text = [[taskList objectAtIndex:indexPath.row] objectForKey:#"age"];
return cell;
}
do this instead:
NSString *desc = [[taskList objectAtIndex:indexPath.row] objectForKey:#"desc"];
NSString *age = [[taskList objectAtIndex:indexPath.row] objectForKey:#"age"];
cell.textLabel.text = [desc stringByAppendingString:age];
return cell;
Another example, which formats the string (in this case the only difference is that I'm adding a space between the two but it introduces you to a very very helpful method) (and uses the two strings that we created above):
NSString *textForMyLabel = [NSString stringWithFormat:#"%# %#", desc, age];
cell.textLabel.text = textForMyLabel;
Or to do the same thing without the temporary variable textForMyLabel use:
cell.textLabel.text = [desc stringByAppendingFormat:#" %#", age];
In the code you've posted, you'll never get to the 'age' portion since it will return after setting 'desc'. Even if you fix that, you're still assigning desc and age to the same field in the cell, which isn't likely to be what you want.

iOS NSArray of NSDictionaries from SQLite in UITableView

I have been struggling for the last two days with this problem and I can't quite seem to figure it out. I have a SQlite database with the following structure.
it's a one to many relationship between List and List_Items
I access the database and create an object that is then added to a NSDictionary which is added to a NSArray. I do this twice, once for the List table and once for the List_Items.
Then I use the list Array to count the number of lists for my tableview rows, I then add them to the tableview.
The problem comes in when I get to the method
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
I can't seem to figure out how to match up the records from Lists and List_Items to show the List Items that pertain to that list in a drill down tableview.
Specific code examples would be helpful as my mind is mush at this point :(
Here is the related code that I have up to my current Writersblock.
//#******************************************************#
// *******Start Database*******
//#******************************************************#
-(void)checkAndCreateDatabase
{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success) return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
}
-(void)readItemsFromDatabase
{
// Setup the database object
sqlite3 *database;
// Init the Items Array
items = [[NSMutableArray alloc] init];
lists = [[NSMutableArray alloc] init];
//---------------### SELECT THE LISTS #####---------------//
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"SQL Opened");
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "SELECT * from List";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *aListName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aUserID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *aListID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
NSLog(#"SQL Compiled");
// Create a new list object with the data from the database
List *list = [[List alloc] initWithlistName:(NSString *)aListName userID:(NSString *)aUserID listID:(NSString *)aListID];
listNames = [NSDictionary dictionaryWithObjectsAndKeys:list.listName,#"listName",list.listID,#"listID",list.listID,#"listID",nil];
// Add the Shopping object to the list Array
[lists addObject:listNames];
}
}
else { NSLog(#"Database Not Found");}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
//---------------### SELECT THE LIST_ITEMS #####---------------//
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"SQL Opened");
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "SELECT * from List_Items";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *aBrandName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aItemName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *aItemQuantity = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 4)];
NSString *aImageUrl = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 5)];
NSString *aListID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 6)];
NSLog(#"SQL Compiled");
// Create a new items object with the data from the database
Shopping *shopping = [[Shopping alloc] initWithlistID:(NSString *)aListID brandName:(NSString *)aBrandName itemName:(NSString *)aItemName itemQuantity:(NSString *)aItemQuantity imageURL:(NSString *)aImageUrl];
itemList = [NSDictionary dictionaryWithObjectsAndKeys:shopping.listID,#"listID",shopping.brandName,#"brandName",shopping.itemName,#"itemName",shopping.itemQuantity,#"itemQuantity",shopping.imageURL,#"imageURL",nil];
// Add the Shopping object to the items Array
[items addObject:itemList];
}
}
else { NSLog(#"Database Not Found");}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
NSLog(#"%#",items);
NSLog(#"%#",lists);
}
sqlite3_close(database);
}
//#******************************************************#
// *******END Database*******
//#******************************************************#
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int rowcount;
rowcount = [lists count];
return rowcount;
}
-(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];
}
// Set up the cell...
NSString *cellValue = [[lists objectAtIndex:indexPath.row] objectForKey:#"listName"];
cell.textLabel.text = cellValue;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[lists objectAtIndex:indexPath.row] objectForKey:#"listID"] != NULL)
{
NSString *listIndex = [[lists objectAtIndex:indexPath.row] objectForKey:#"listID"];
int i = [listIndex intValue];
NSLog(#"indexPath: %d",i);
}
}
EDIT**********
The single Sql statement returns more than one Listname. This is an issue because I only need one of each list name.
So first of all, You're creating a List object and then creating an NSDictionary object that's pretty much the same as the List Object. Why? Why not just add the List object to the the Array. If you're not performing any functions on the properties in the List item, then don't use a List object at all, just put the fields directly in the NSDictionary.
Secondly, don't do two different SQL calls to get the info, use only one to get the List and the list_items for that list at the same time. Then if you're using your List object, add a NSMutableArray property call items, and add your listItems to that array. You can do the same thing in an NSDictionary, just add an NSMutableArray object for key items, then add the list_items to that array.
Now you'll be able to setup the tableview to do what you want.
Amended answer in response to comments below
Select * FROM List, List_Items WHERE List.list_id = List.list_id
Anyone interested this is how I ened up figuring it out.
I get the listID for the list then I make an array and loop through the list items an compair them against the listID. If they are the same as the listID I add them to the array and once it's finished with the for loop it adds the results to my dataobject protocol (to make it available to the next view) then I present a modal view controller and load the array from the dataobject. Once i'm done with the modal view I set the dataobject back to nil and Tada! it works.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[lists objectAtIndex:indexPath.row] objectForKey:#"listID"] != NULL)
{
[listTableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *listIndex = [[lists objectAtIndex:indexPath.row] objectForKey:#"listID"];
NSMutableArray *itemArray = [[NSMutableArray alloc]init];
int i; int itemCount = [items count];
for (i = 0; i < itemCount; i++)
{
if ([[[items objectAtIndex:i] objectForKey:#"listID"] isEqual:listIndex])
{
[itemArray addObject:[items objectAtIndex:i]];
NSLog(#"Item Array:%#",itemArray);
}
}
if (i == itemCount)
{
AppDataObject* theDataObject = [self theAppDataObject];
theDataObject.itemArray = itemArray;
ItemView *temp = [[ItemView alloc] initWithNibName:#"ItemView" bundle:nil];
[self presentModalViewController: temp animated: YES];
}
}
}

NSDictionary to TableView

because i'm a newby at Stackoverflow i cannot comment someones anwser yet. (my reputation is 16..). I got a question about this anwser: How do I put this JSON data into my table view? Please help me, I'm living in a nightmare :)
Fulvio sais you have to use [eventNameList addObject:event]; and [eventNameList objectAtIndex:indexPath.row]; to store and get the event data but. addObject is an NSMutableSet method and objectAtIndex:indexPath.row is not. So i cannot use this method to get the data from the NSMutableSet.
Besides that, i can use the count methods neither.
Any Idea's ?
Assuming you have an NSDictionary, you could use the [dictionary allKeys] method to retrieve an array with all keys (lets call it keyArray for now). For the rowCount you could return the count of objects in this keyArray. To get the item that needs to be displayed in the cell you could use [dictionary objectForKey:[keyArray objectAtIndex:indexPath.row]]] to get the appropriate dictionary for the displayed cell.
In code:
// use the keyArray as a datasource ...
NSArray *keyArray = [jsonDictionary allKeys];
// ------------------------- //
// somewhere else in your code ...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [keyArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// set some cell defaults here (mainly design) ...
}
NSString *key = [keyArray objectAtIndex:indexPath.row];
NSDictionary *dictionary = [jsonDictionary objectForKey:key];
// get values from the dictionary and set the values for the displayed cell ...
return cell;
}
#Tieme: apparantly the URL you use already returns an array, you don't really need to process a dictionary (you could just use the array as the dataSource), check out the following:
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSURL *url = [NSURL URLWithString:#"http://www.my-bjoeks.nl/competitions/fetchRoutes/25.json"];
NSString *string = [[[NSString alloc] initWithContentsOfURL:url] autorelease];
NSError *jsonError = nil;
id object = [json objectWithString:string error:&jsonError];
if (!jsonError) {
NSLog(#"%#", object);
NSLog(#"%#", [object class]); // seems an array is returned, NOT a dictionary ...
}
// if you need a mutableArray for the tableView, you can convert it.
NSMutableArray *dataArray = [NSMutableArray arrayWithArray:object]
eventNameList should be defined as an NSMutableArray, not an NSMutableSet. NSMutableArray responds to both -addObject (it puts the new object at the end of the array) and -objectAtIndex: and when you think about it, a table view is essentially an ordered list and so is an array whereas a set is not.
LUCKY:)
Assuming that you might be having nsmutablearray of nsdictionary.
In such case you can get data using:
[dictionary objectforkey:#"key"] objectAtIndex:indexpath.row]