I use coredata to fill an array with data that is displayed in a tableview.
In the tableview, I have two section.
When a cell is pushed in section1, I want that cell to be moved to section2, and the other way around.
Im not quite sure how to accomplish this, and I been sitting trying to figure it out for about 8 hours now.
This is what I got so far:
I use this code to get the data:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"creationDate" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:&sortDescriptor count:1];
NSMutableArray *sortedIngredients = [[NSMutableArray alloc] initWithArray:[event.tags allObjects]];
[sortedIngredients sortUsingDescriptors:sortDescriptors];
self.tagsArray = sortedIngredients;
[sortDescriptor release];
[sortDescriptors release];
[sortedIngredients release];
In didSelectRowForIndexPath, I figure out how to remove the cell:
Tag *tag = [tagsArray objectAtIndex:indexPath.row];
NSManagedObjectContext *context = event.managedObjectContext;
[context deleteObject:tag];
[tagsArray removeObject:tag];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
How do I insert this cell in section 2 in the tableView?
Should I create 2 arrays?
I tried some code that used NSDictiorary, like this:
[tableView addObject: sectionOneObject forKey:#"Section1"];
[tableView addObject: sectionTwoObjects forKey: #"Section2"];
But I never manage to get it to work.
Any help would be highly appreciated!
Thanks in advance
Check out -insertRowsAtIndexPaths:withRowAnimation, e.g.:
unsigned _insertionIndices[2] = {1,0}; // second section, first row
NSIndexPath *_insertionIndexPath = [[NSIndexPath alloc] initWithIndexes: _insertionIndices length:2];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:_insertionIndexPath] withRowAnimation: UITableViewRowAnimationRight];
[_insertionIndexPath release];
Related
i'm getting the distance of 2 points like this
[userLocation distanceFromLocation: annotationLocation] / 1000;
and setting this to the subtitle of a tableview like the image bellow
the question is, can i order this table by the distances (subtitle)?
Thanks!
and sorry for the bad english =x
You can order your UITableView's cells any way to want to, but you have to do it before showing them, when you create the table's data source. If you use an NSFetchResultsController, you can put the distance as the sort descriptor. And if you are using a simple NS(Mutable)Array, sort it before making it the table's source.
Like Chris said, you can do it with
NSSortDescriptor *titleSorter= [[NSSortDescriptor alloc] initWithKey:#"annotationLocation" ascending:YES];
If it is an NSArray what you are using, then:
[arrayOfObjects sortUsingDescriptors:[NSArray arrayWithObject:titleSorter];
and if it is an NSFetchResultsController:
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:titleSorter, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
Create an NSSortDescriptor to sort your rows:
NSSortDescriptor *titleSorter= [[NSSortDescriptor alloc] initWithKey:#"annotationLocation" ascending:YES];
[arrayOfObjects sortUsingDescriptors:[NSArray arrayWithObject:titleSorter];
Make an array with these distances, order it how you want and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *_cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (_cell == nil) {
_cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier] autorelease];
}
_cell.textLabel.text = #"Transcripts";
_cell.detailTextLabel.text = [yourArray objectAtIndex:indexPath.row];
return _cell;
}
well, something like that should do the trick.
Hope it helps
Assuming you have some certain objects holding a coordinate and put the objects in to an array locations, you can use a comparator block by doing:
locations = [locations sortedArrayUsingComparator: ^(id a, id b) {
CLLocationDistance dist_a= [[a objectsForKey:#"coordinate"] distanceFromLocation: userPosition];
CLLocationDistance dist_b= [[b objectsForKey:#"coordinate"] distanceFromLocation: userPosition];
if ( dist_a < dist_b ) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( dist_a > dist_b) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}
You add this code to your viewWillAppear: to get updated locations each time you display the tableView.
I am a noob when it comes to traversing Core Data many-to-many relationships (I have read numerous threads and documentation on this so unless it's and end all, please no links to documentation or threads).
I am making an inventory application and currently have a Core Data model that includes an "Thing", "Category", "Location", and "ThingLocation" (ThingLocation is an entity that holds both a Thing and Location reference but includes the amount of Things on that particular Location. Also a many-to-many relationship) Entities that I would like to populate my UI with. I am proficient in GUI so this is not a question of User Interface but rather how I would gather the information using (probably) NSPredicates.
Ex: If I show a TableView consisting of a Category entity's details then how would I populate it with the Things in that Category Entity.
Ex: If I wanted to display a UILabel showing the total amount of Thing's there were in it. (i.e. add up all of the amounts on each Location).
EDIT: I want to be able to use an NSFetchedResultsController!
I am not exactly sure what your question is asking. So for example you want to iterate over all the categories and all the things in that category you would first do a request for the entities of category without a predicate (this will return all category objects) and iterate over all those with fast enumeration:
//iOS 5 way of doing it
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntity:#"Category"];
NSArray* arrayOfObjects = [context executeFetchRequest: request withError: nil];
for (Category* cat in arrayOfObjects)
{
//iterate over all the things in that category
for (Thing* thing in cat.things){
{
//do something?
}
}
For your first example of populating a tableview with things in a category,
If you have the category you would get the Things very easily like this:
NSSet* things= category.things;
//you can get it into an array by sorting it somehow or just get it like that.
NSMutableArray* things = [[category.things allObjects] mutableCopy];
You can iterate over this in a very normal fashion or use them as your datasource for your tableview. If you don't have the category you need something to distinguish it in which case you would set up the predicate like this:
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntity:#"Thing"];
NSPredicate* pred = [NSPredicate predicateWithFormat:#"(relationToCat.DestAtt = %#)",howToDestinguish];
This will return all the Things that are connected to a category that has that attribute.
For your second example, You would set the NSPredicate up to get all the ThingLocations for that specific Thing. Then iterate over them and add up the values at the locations. If you wanted to do this for every category over everything it would just require you to nest some for loop starting with the categories. then for each thing get all the ThingLocations and for each of those add up the values.
I hope that answers your questions. To-Many relations are just sets and can be treated as such. I find that thinking from the bottom up helps me form the predicates. thinking I need all the things in this category so I would set up with the entity of Things and connecting it back to the category in the predicate.
Edit: NSFetchedResultsController example
In your .h file after declaring your super class add NSFetchedResultsControllerDelegate to the delegates implemented.
Create an ivar:
#property (nonatomic, retain) NSFetchedResultsController* fetchedResultsController;
On the implementation side I've seen two different approaches, the first is writing a custom accessor for the property that initializes it there, the other is just to do it in the viewDidLoad in either case the setup is as follows:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Thing" inManagedObjectContext:context];
NSPredicate* pred=[NSPredicate predicateWithFormat:#"(relToCat.something=%#)",something];
[fetchRequest setPredicate:pred];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20]; //tells it how many objects you want returned at a time.
//this is for displaying the things in some sort of order. If you have a name attribute you'd do something like this
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects: descriptor, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
//this is the set up. If you put a sectionNameKeyPath it will split the results into sections with distinct values for that attribute
NSFetchedResultsController *frc = nil;
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context] sectionNameKeyPath:#"attribute that splits it into sections" cacheName:nil];
[frc setDelegate:self];
[self setFetchedResultsController:frc];
[frc release];
frc = nil;
//Tells it to start.
[fetchedResultsController performFetch:nil];
Then for the table view delegate methods it is a piece of cake like so:
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return [[[fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
return [[fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[fetchedResultsController sections] objectAtIndex: section] numberOfObjects];
}
/* If you want the bar on the right with the names of the sections...
-(NSArray*) sectionIndexTitlesForTableView:(UITableView *)tableView{
return [fetchedResultsController sectionIndexTitles];
}*/
-(UITableViewCell* )tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* ident=#"cellident";
UITableViewCell *cell = [self.itemTable dequeueReusableCellWithIdentifier:ident];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:ident] autorelease];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void) configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath {
NSManagedObject* item=[fetchedResultsController objectAtIndexPath:indexPath];
//set up your cell somehow
return cell;
}
You also need to add the delegate methods for the fetched results controller. They are all very simple and look something like this:
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
[tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController*) controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
NSIndexSet *set = [NSIndexSet indexSetWithIndex:sectionIndex];
switch(type) {
case NSFetchedResultsChangeInsert: [tableView insertSections:set withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteSections:set withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath*)newIndexPath
{
UITableView *tv = tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tv cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller {
[tableView endUpdates];
}
This will update the table if in some other part of the program a thing is added it will automatically show up on the table if the predicate matches.
I am adding object exercise to object session (as a relationship).
In a view, I want to fetch and display exercises for a particular session object.
Right now it is showing all exercises in the database rather than just for that session object.
The relationship between the two objects is called "exercises".
This is the current code I am using for the fetch if anyone can help me.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Exercise" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
LogResultsViewController *logResultsTableViewController = [[LogResultsViewController alloc]init];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat: #"exercises = %#", logResultsTableViewController.selectedSession]];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
Updated Code:
- (void) viewDidLoad
{
NSSet *exercises = [self.selectedSession valueForKey:#"exercises"];
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"timeStamp" ascending:YES]];
NSArray *sorted = [exercises sortedArrayUsingDescriptors:sortDescriptors];
sorted = self.exerciseArray;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [exerciseArray count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (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] autorelease];
}
cell.textLabel.text = [exerciseArray objectAtIndex:indexPath.row];
return cell;
}
When I NSLog the selectedSession, here is what it shows:
selectedSession: <Session: 0x7336940> (entity: Session; id: 0x7334f70 <x-coredata://17D44726-23F7-402F-9CBE-2EED96212E14/Session/p1> ; data: {
exercises = "<relationship fault: 0x5d34680 'exercises'>";
timeStamp = "2011-05-31 04:41:07 +0000";
Also, when I NSLog the NSSset called exercises, I get:
Relationship fault for (<NSRelationshipDescription: 0x711b370>), name exercises, isOptional 1, isTransient 0, entity Session, renamingIdentifier exercises, validation predicates (
), warnings (
), versionHashModifier (null), destination entity Exercise, inverseRelationship exercises, minCount 0, maxCount 0 on 0x7140790
Update:
Ok so I changed the code in cellForRowAtIndex to have
Exercise *exercise = (Exercise *)[exerciseArray objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.name;
Now it shows the exercise name but it is showing the same list of exercises for all sessions, instead of just for the session to which it belongs to.
If your instances of Session have a relationship to instances of Exercise, you don't need to do another fetch to get the session's exercises. You can just follow the relationship and get them directly-- they'll be loaded automatically. From your code it looks like logResultsTableViewController.selectedSession is an instance of Session. If that's the case, you can get all of that session's exercies as follows (assuming the relationship is called exercises):
NSSet *exercises = [logResultsTableViewController.selectedSession valueForKey:#"exercises"];
You can then sort that NSSet as needed.
I am creating Travel guide app using core data. I have 4 entities CITY ,RESTAURANTS ,
HOTEL AND FAMOUS PLACES. City is connected with all other entity because one city may have number of restaurants , hotels and places. City entity has 3 attribute Name ,Image ,Description. I am able to display list restaurants of selected city.In Restaurant Entity I have 4 attribute Name, Description ,Address and phone no..Now I want to show
this attribute detail of selected restaurant(of selected city) in next view.But how can I access restaurant description in next view..
Here is my code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSMutableArray *restaurants = [[NSMutableArray alloc]initWithArray:[city.cityTorestaurants allObjects]];
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc]initWithKey:#"Name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:nameDescriptor,nil];
[restaurants sortUsingDescriptors:sortDescriptors];
[hotels sortUsingDescriptors:sortDescriptors];
[self setArrayOfRestaurants:restaurants];
[restaurants release];
[nameDescriptor release];
[sortDescriptors release];
[tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier]autorelease];
}
//set up cell
NSLog(#"For Restaurants List");
Restaurants *restaurants = [arrayOfRestaurants objectAtIndex:indexPath.row];
cell.textLabel.text = restaurants.Name;
return cell;
}
cityTorestaurants and restaurantTocity is relationship in core data..
Help Please..
In your RestaurantViewController create a property Restaurant* currentRestaurant
in your didSelectRowAtIndexPath:of the CityViewController set the property with self.restaurantVC.currentRestaurant = [arrayOfRestaurants objectAtIndex:indexPath.row];
I have a refresh button in the toolbar that regenerates an NSArray and attempts to reload the contents of the table.
However, whether I have:
if (boolCondition) {
[self refreshTableDataSet];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kTableSectionOfInterest] withRowAnimation:UITableViewRowAnimationFade];
}
or:
if (boolCondition) {
[self refreshTableDataSet];
[self.tableView reloadData];
}
About every other attempt to refresh the table fails. Sometimes it works, sometimes not.
I have some NSLog statements in -tableView:cellForRowAtIndexPath: to let me know when this method is being fired. When the refresh fails, I do not see the output of these NSLog statements.
Is there something I'm missing about reloading a table view, when I have new data?
EDIT
In my -refreshTableDataSet method:
- (void) refreshTableDataSet {
NSSortDescriptor *_objectTypeSorter = [[[NSSortDescriptor alloc] initWithKey:#"object.type" ascending:YES] autorelease];
NSSet *_objectSet = [managedObjectContext fetchObjectsForEntityName:#"Object" withPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"group.name like '%#'", [group name]]]];
self.objects = [[[_objectSet allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObjects: _objectTypeSorter, nil]] retain];
}
In my table view -tableView:cellForRowAtIndexPath: method:
...
Object *_object = [self.objects objectAtIndex:indexPath.row];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = _object.name;
cell.detailTextLabel.text = _object.type;
cell.imageView.image = [UIImage imageNamed:#"GroupType.png"];
cell.accessoryView = [self imageViewForObjectDetailType:_object.type];
...
I have a method for the accessoryView called -imageViewForObjectDetailType, which just returns a UIImageView:
return [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"GenericObjectDetailType.png"]] autorelease];
Are there retain and/or release messages that I am missing?
I'm sure you've checked this, but I see random behavior like this if I have an NSArray that wasn't retained and passed on to a tableview.
Looking at your added code, I don't see anything wrong at first glance - here are some tips:
1.In:
self.objects = [[[_objectSet allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObjects: _objectTypeSorter, nil]] retain];
If objects is a #property with retain attribute, then you don't need to retain it here again. Not why it would fail though.
2.In your cellForRowAtIndexPath:, you do have something like:
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
just before the if (cell == nil), right?
3.You might want to NSLog the value of object when you get it from the array in
Object *_object = [self.objects objectAtIndex:indexPath.row];
4.Break this up onto multiple lines and look at all the return values with NSLog to make sure they all succeed every time you refresh.
self.objects = [[[_objectSet allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObjects: _objectTypeSorter, nil]] retain];
5.Double check this to make sure your fetch is getting you what you expect:
NSSet *_objectSet = [managedObjectContext fetchObjectsForEntityName:#"Object" withPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"group.name like '%#'", [group name]]]];
You can NSLog the [_objectSet count] to watch to see whether an unsuccessful refresh gives you a bad count.
Hope this helps.