I have an NSMutableArray to has the "many" side of a one-to-many relationship. I have it loaded into a UITableView. When I edit the table, by tapping "edit" I have the ability to add a new row to the table, which means add a new row to the NSMutableArray. The table Segue's to a new UITableView to allow the data to be entered. I want to be able to save the data into the NSMutableArray. What is the best way of doing this?
Thanks in advance.
To add objects to an NSMutableArray:
NSMutableArray * testArray;
testArray = [NSMutableArray arrayWithCapacity:10];
[testArray addObject:#"one"];
[testArray addObject:#"three"];
[testArray insertObject:#"two" atIndex:1];
Related
I think I'm just missing something obvious here, but it's one of those frustrating things that's somehow eluding me.
I have a Core Data Entity called ProjectEntry. The ProjectEntry objects are displayed in uitableviews, using various attributes, arranged by date (attributes include things like "dateAsNSDate"[NSDate], "month"[NSString], "year"[NSString], "dayOfWeek"[NSString]).
I'm using an NSFetchedResultsController to populate the table views.
When I initially create and save the ProjectEntry object, the "dateAsNSDate" attribute is parsed and converted into various NSStrings. One string, also an attribute, is called "concatMonthAndYear". It takes the "month" and "year" strings and just joins them. So I get things such as "January 2014", "February 2015", etc.
I use the "concatMonthAndYear" as my cell.textLabel.text string to display in my tableview cells.
I use the NSDate attribute to sort the tableview rows (sortDescriptor), and the "year" attribute as my section headers (sectionNameKeyPath).
So right now, I'd have a tableview section called "2014", with tableview rows each representing a Core Data object, named things like "January 2014", February 2014", etc, in said section.
I can tap on one of those rows, segue to another tableview, and list all objects created in January 2014, for example, by using an NSPredicate on the second tableview.
However, on my first tableview, each Core Data object created is represented by its own tableview row. So I'll get multiple rows reading "January 2014" or "May 2015" or whatever. They're valid saved objects, and I want them, but I'd like to prevent a new row from being created if that "concatMonthAndYear" already exists. If a row titled "January 2014" already exists, I don't want a new row created. I want the new Core Data object stored, just not a new tableviewrow representing it. I only need one row with "January 2014", for example, to segue into a table listing ALL the entities from January 2014.
I know how to use an NSPredicate to get ALL the January 2014 objects into the second table, but how do I get JUST ONE object into the first table?
Is NSPredicate not the right device for that? Should I be somehow preventing a new cell from being created in the UITableView delegate methods? Each tableview row should be unique, and I'm stuck on whether it should be handled with the NSFetchedResults controller or in the tableview delegate methods?
Or some other way?
Can someone point in the right direction?
EDITED TO INCLUDE CODE:
- (void)setupFetchedResultsController
{
// 1 - Decide which Entity
NSString *entityName = #"ProjectEntry";
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
[request setReturnsDistinctResults:YES];
[request setResultType:NSDictionaryResultType];
[request setPropertiesToFetch:#[#"monthYearTableSecHeader", #"year"]];
// 3 - Filter it
//request.predicate = [NSPredicate predicateWithFormat:#" "];
// 4 - Sort it
request.sortDescriptors = [NSArray arrayWithObjects:[NSSortDescriptor sortDescriptorWithKey:#"year"
ascending:NO],
[NSSortDescriptor sortDescriptorWithKey:#"dateAsNSDate"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)], nil];
//5 - Fetch
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"year"
cacheName:nil];
[self performFetch];
}
You could use
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:#[#"concatMonthAndYear", #"year"]];
This will cause the fetch request to return distinct dictionary objects corresponding to "January 2014", etc. objects.
However, you cannot use a fetch request controller's delegate methods (to hear of updates to the data).
If you need to hear updates, I suggest you add a layer of indirection to your data, where MonthEntry is an object representing yearly months and have a one to many relationship with ProjectEntry, which is your normal entity. This way, you can set the fetch request entity to MonthEntry.
SCENARIO
I have two entities: Item and ListDetail (which contains prices for different lists for every item). This is absolutely needed and I can't provide a price attribute for the Item entity because every item can have more prices for different dynamic lists (retail, b2b ecc.).
The relationship is:
Item (lists) <------->> (item) ListDetail
The current active list in my app change dinamically, so let's say I have an integer variable with the current active list: _ACTIVE_LIST_CODE_. When I need a price for an item object I use an helper method on the Item class:
-(NSNumber*) getPrice {
NSSet *lists=[self.lists filteredSetUsingPredicate: [NSPredicate predicateWithFormat:#"listId == %d",_ACTIVE_LIST_CODE_]];
ListDetail *activeList=[[lists allObjects] objectAtIndex:0];
return activeList.price;
}
THE PROBLEM
I use a UITableView with NSFetchedResultController in order to select and show some items for different sections. Nothing special. I would like to order the fetchedObjects using the items price for the active list. If price was an attribute of Item I would added simply a sort descriptor to the fetch request like so:
[NSSortDescriptor sortDescriptorWithKey:#"price" ascending:YES];
But as said before this is not possible, price is a dynamic attribute.
If using transient properties was possible for sort descriptors, I would set a price transient properties calculated on fly using my helper method. Nothing to do.
Using a keypath in the descriptor like "lists.price" is not possible (or maybe I don't know how to do that), just because it's a to-many relationship and it's modeled with a NSSet.
I tried some workaround, without success:
1) observing _ACTIVE_LIST_CODE_ changes to set items price in a non-transient attribute.
2) after the fetch request, before presenting the table view, reorder a brand new array with fetched objects using the transient "price" property, iterate the orderdered array following an ascending integer index "i" and assigning this value to a non-transient property "order" for the Item entity. Using "order" for sort descriptor in the fetch request. (This approach is described here: Re-ordering NSFetchedResultsController)
Both of them works, but they slow down performance because I have thousands of items in the fetch results... Any idea?
How about fetching ListDetail instead? You could restrict and sort with the appropriate predicates and sort descriptors, exactly as you propose.
fetchRequest.predicate =
[NSPredicate predicateWithFormat:#"listID = %#", activeListCode];
fetchRequest.sortDescriptors =
#[[NSSortDescriptor sortDescriptorWithKey:#"price" ascending:YES]];
Now, to group by some attribute of item should be simple and efficient because it is a to-one relationship. Your fetched results controller's sectionNameKeyPath can be something like
#"item.category"
I have 1 table with in which i need to compare the values. If they dont match highlight those table view cell.
Table has column
XXX YYYY
MH_01 MH_02
MH_02 MH_03
MH_04 Mh_05
What it need to check is that row[1]YYYY=row[2] XXX if not then it should highlight that cell in a row. like in this example it will highlight(YYYY-(MH_03) and xxxx(MH_04).
Implement transient properties on your model object for the next and previous entry. Then create two more transient properties on your model object to return a NSColor or whatever you need to display the highlighting of the cell.
You can then either populate the next and prev properties in your table's datasource. If you are using NSArrayController as a datasource, you can easily subclass NSArrayController to populate next and previous for you.
-(NSArray *)arrangeObjects:(NSArray *)objects
{
NSArray * arrangedObjects = [super arrangeObjects:objects];
NSManagedObject * previousEntry;
for (NSManagedObject * entry in arrangedObjects)
{
[previousEntry setValue:entry forKey:#"nextEntry"];
[entry setValue:previousEntry forKey:#"prevEntry"];
previousEntry = entry;
}
return arrangedObjects;
}
I am currently working on a table list with categories and subcategories, where you can navigate using a drilldown to explore the child categories until you get a detail view.
I got inspiration from iphonesdkarticles.com.
The solution in this blog for the infinite drilldown was populating an array with a plist, and a single UITableView to do the drilldown.
I wanted to use core data with the NSFetchedResultsController instead of the plist.
I got the first list of categories, but when I click one of them, I got an empty table.
I don't know if using the NSFetchedResultsController in this scenario is the most appropriate solution. Maybe I am doing something wrong when I use didSelectRowatIndex:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Categories *category = (Categories *)[fetchedResultsController objectAtIndexPath:indexPath];
MasterViewController *theViewController = [[MasterViewController alloc] initWithStyle:UITableViewStylePlain];
theViewController.CurrentLevel += 1;
theViewController.CurrentTitle = categories.name;
detailViewController.category = category;
[self.navigationController pushViewController:theViewController animated:YES];
}
To create infinite drill down in Core Data, just make an entity a relationship of itself.
I have NSDictionaries stored in an NSMutableArray. Each NSDictionary has a NSNumber inside of it. So right now, my tableview will show the cells in the way they are ordered in the array. What I want to do is reorder the cells from highest NSNumber to lowest NSNumber, so how would I do this with all the NSDictionaries in the NSMutableArray. I will probably need a for loop or something like that but I am not sure how to do it.
Also I have NSDate's (non-formatted) in my NSDictionaries, so how would I sort the NSDictionaries based upon the newest dates inside them?
Thanks!
You need to use the sortedArrayUsingSelector method of NSMutableArray and provide a sorting function, like this:
NSMutableArray *yourSortedArray;
yourSortedArray = [originalArray sortedArrayUsingSelector:#selector(yourCompareFunction:)];
...
- (NSComparisonResult)yourCompareFunction:(NSDictionary *)otherObject {
return [[self objectForKey:#"number_key"] compare:[otherObject objectForKey:#"number_key"]];
}
I didn't tested this but that's the idea.