How to Reorder Cells Between Sections with NSFetchedResultsController - tableview

I have a table view that allows users to reorder cells between sections.
I have this, which works in a table with one section, but I can't figure out how to change this to work between sections...
-(void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toIndexPath:(NSIndexPath *)destinationIndexPath;
{
NSMutableArray *things = [[fetchedResultsController fetchedObjects] mutableCopy];
// Grab the item we're moving.
NSManagedObject *thing = [[self fetchedResultsController] objectAtIndexPath:sourceIndexPath];
// Remove the object we're moving from the array.
[things removeObject:thing];
// Now re-insert it at the destination.
[things insertObject:thing atIndex:[destinationIndexPath row]];
// All of the objects are now in their correct order. Update each
// object's displayOrder field by iterating through the array.
int i = 0;
for (NSManagedObject *mo in things)
{
[mo setValue:[NSNumber numberWithInt:i++] forKey:#"displayOrder"];
}
[managedObjectContext save:nil];
}
Please could anyone help?

Related

reading Autosave'd NSSortDescriptor's from NSTableView

I have an application that display an array of objects in a 6 column NSTableView. The user can display the list via whichever column sort order they prefer. The NSTableView in the XIB file uses the autosave feature so that the sort order is preserved over application launches. All is working well.
There are times though when I want to know what the user's sort order preference is before this particular NSTableView get's loaded. I'd like to read those sort ordering preferences out of the Preferences file to sort the array according to their preference, but without having loaded from the XIB file that contains that NSTableView.
I can see the sort descriptors saved in my app's standardUserDefaults Preferences file. It's an array with 12 items in it (0-11) and the even numbered elements (0,2,4,6,8,10) are shown as Data and the odd numbered elements(1,3,5,7,9,11) are Boolean's.
The documentation says that NSSortDescriptor conforms to NSSecureCoding, but if I instantiate an NSKeyedUnarchiver with one of the Data elements, I get:
"non-keyed archive cannot be decoded by NSKeyedUnarchiver"
If I use an NSUnarchiver, I get:
"* End of archive encountered prematurely at 58"
In either case, the Boolean elements in the array are the "ascending" flag property of the NSSortDescriptor and so it wouldn't even be included if doing the above. So I guess I need to combine the 2 elements in some way to get a coded sort descriptor? Does anyone have an idea of how to decode the "autosaved" sort descriptors?
this solution worked for me: (please ignore all the ZSAsserts)
its a method in a subclass/category of NSTableView
-(void) zsSetTableLayoutFromAutosaveName:(NSString*) autosaveName{
// set initial sortDescriptors
int i = 0;
NSString* columnName = #"";
NSMutableArray* sortDescriptors = [NSMutableArray new];
for (id data in [[NSUserDefaults standardUserDefaults] valueForKey:[NSString stringWithFormat:#"NSTableView Sort Ordering %#",autosaveName]]) {
if (i%2 == 0) {
ZSAssert([data isKindOfClass:[NSData class]]);
columnName = [NSUnarchiver unarchiveObjectWithData:data];
}
else {
ZSAssert([data isKindOfClass:[NSNumber class]]);
BOOL ascending = [((NSNumber*) data) boolValue];
[sortDescriptors addObject:[NSSortDescriptor sortDescriptorWithKey:columnName ascending:ascending]];
}
i++;
}
[self setSortDescriptors:sortDescriptors];
// set intial hidden-state
for (NSData* data in [[NSUserDefaults standardUserDefaults] valueForKey:[NSString stringWithFormat:#"NSTableView Hidden Columns %#",autosaveName]]) {
ZSAssert([data isKindOfClass:[NSData class]]);
NSString* colName = [NSUnarchiver unarchiveObjectWithData:data];
ZSAssert([colName isKindOfClass:[NSString class]]);
NSTableColumn* tc = [self tableColumnWithIdentifier:columnName];
if (!tc.isHidden){
[tc setHidden:YES];
}
}
// set initial width
i = 0;
for (id data in [[NSUserDefaults standardUserDefaults] valueForKey:[NSString stringWithFormat:#"NSTableView Columns %#",autosaveName]]) {
if (i%2 == 0) {
ZSAssert([data isKindOfClass:[NSData class]]);
columnName = [NSUnarchiver unarchiveObjectWithData:data];
}
else {
ZSAssert([data isKindOfClass:[NSString class]]);
float width = ((NSString*) data).floatValue;
[[self tableColumnWithIdentifier:columnName] setWidth:width];
}
i++;
}
[self setAutosaveName:autosaveName]; // from now on, every change made to the table should be automatically persisted into user-defaults
[self setAutosaveTableColumns:YES];
}

UICollectionView reloadItemsAtIndexPaths

I've been trying to wrap my head around the reloadItemsAtIndexPaths method of UICollectionView.
I have an array of objects called objectsArray. When a user scrolls to the bottom of my collection view, I fetch the next batch of objects from the backend and append it to objectsArray by calling [objectsArray addObjectsFromArray:objects]; After doing so, I call [self.collectionView reloadData] which I know is expensive.
I'd like to optimize with the code below but I get an assertion failure when calling reloadItemsAtIndexPaths.
if (self.searchPage == 0) {
parseObjectsArray = [[NSMutableArray alloc] initWithArray:relevantDeals];
[self.collectionView reloadData];
} else {
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
for (int i = 0; i < [relevantDeals count]; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
[indexPaths addObject:indexPath];
}
[parseObjectsArray addObjectsFromArray:relevantDeals];
[self.collectionView reloadItemsAtIndexPaths:indexPaths];
//[self.collectionView reloadData];
}
Error:
* Assertion failure in -[UICollectionView _endItemAnimations], /SourceCache/UIKit/UIKit-2903.2/UICollectionView.m:3716
Any help/tips is greatly appreciated.
Thanks!
It looks like the items being added are new, in other words you are adding items which weren't there before. In that case, just replace reloadItemsAtIndexPaths:indexPaths with insertItemsAtIndexPaths:
[self.collectionView insertItemsAtIndexPaths:indexPaths]; // Use this for newly added rows
//[self.collectionView reloadItemsAtIndexPaths:indexPaths]; // Use this for existing rows which have changed
The assertion error was misleading. It wasn't until after I implemented
#try
{
[self.collectionView insertItemsAtIndexPaths:indexPaths];
}
#catch (NSException *except)
{
NSLog(#"DEBUG: failure to insertItemsAtIndexPaths. %#", except.description);
}
Did I realize was calculating my indexPaths wrong. My index offset for the new objects was off by one making my collectionview think I was adding more objects than I specified in numberOfItemsInSection.
Thanks for all those who attempted to help!
This error often arises when you increase/decrease the number of items in the array but it doesn't match the data source method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Are you updating it appropriately?

Add more UICollectionViewCell to an existing UICollectionView

I'm trying to add some more cells to an existing UICollectionView, which is already filled with some cells.
I tried to use the CollectionView reloadData but it seems to reload the entire collectionView and I just wanted to add more cells.
Can anybody help me?
The UICollectionView class has methods to add/remove items. E.g., to insert an item at some index (in section 0), modify your model accordingly and then do:
int indexPath = [NSIndexPath indexPathForItem:index];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath inSection:0];
[collectionView insertItemsAtIndexPaths:indexPaths];
The view will do the rest.
The easiest way to insert new cells to the UICollectionView without having to reload all its cell is by using the performBatchUpdates, which can be done easily by following the steps below.
// Lets assume you have some data coming from a NSURLConnection
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *erro)
{
// Parse the data to Json
NSMutableArray *newJson = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
// Variable used to say at which position you want to add the cells
int index;
// If you want to start adding before the previous content, like new Tweets on twitter
index = 0;
// If you want to start adding after the previous content, like reading older tweets on twitter
index = self.json.count;
// Create the indexes with a loop
NSMutableArray *indexes = [NSMutableArray array];
for (int i = index; i < json.count; i++)
{
[indexes addObject:[NSIndexPath indexPathForItem:i inSection:0]];
}
// Perform the updates
[self.collectionView performBatchUpdates:^{
//Insert the new data to your current data
[self.json addObjectsFromArray:newJson];
//Inser the new cells
[self.collectionView insertItemsAtIndexPaths:indexes];
} completion:nil];
}

Updating Checkboxes In NSTableView With A "Select All" Checkbox

I've been searching, reading, trying different approaches, etc. for the last couple weeks trying to solve this issue, but no luck... Any help is appreciated.
I have an NSTableView which uses my controller class as it's datasource, an NSMutableArray(validPropertyList) of NSDictionaries to be more exact. I have the dictionaries holding one NSNumber numberWithBool with a key of #"checkboxValue", and one NSString with a key of #"address". I initially set all the checkboxValues to YES.
for (int i =0; i<properties.count; i++) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:[properties objectAtIndex:i],#"address",[NSNumber numberWithBool:YES],#"checkboxValue", nil];
[validPropertyList addObject:dict];
}
My table has 2 columns, whose identifiers correspond to the key names of my dictionaries.
In IB, I dragged over a Check Box Cell into the "checkboxValues" column. The required methods are below for the table.
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return [validPropertyList count];
}
-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
NSString *identifier = [tableColumn identifier];
return [[validPropertyList objectAtIndex:row]valueForKey:identifier];
}
All of that works fine. The table loads a (selected)checkbox in the first column and my string object in the other one for each dictionary in my array.
I implemented the below method in order to handle the state changes of the checkboxes in the table...
-(void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[validPropertyList objectAtIndex:row]];
[dict setValue:object forKey:#"checkboxValue"];
[validPropertyList replaceObjectAtIndex:row withObject:dict];
// NSLog(#"val is now %#",[[validPropertyList objectAtIndex:row]valueForKey:#"checkboxValue"]);
}
This appears to work as well. Clicking on a checkbox visually changes it's state. It also updates the [NSNumber numberWithBool] value in my array as it should.
I have a checkbox outside of the table that I'm trying to use to either select all or deselect all of the checkboxes in the table. I have a bool variable(selectAllValue) in my class that is bound to the state of this checkbox, and this checkbox calls the action below...
- (IBAction)selectAll:(id)sender {
if (selectAllValue == YES) {
for (int i=0; i<[validPropertyList count]; i++) {
NSMutableDictionary *dict = [validPropertyList objectAtIndex:i];
[dict setValue:[NSNumber numberWithBool:YES] forKey:#"checkboxValue"];
[validPropertyList replaceObjectAtIndex:i withObject:dict];
}
}
else{
for (int i=0; i<[validPropertyList count]; i++) {
NSMutableDictionary *dict = [validPropertyList objectAtIndex:i];
[dict setValue:[NSNumber numberWithBool:NO] forKey:#"checkboxValue"];
[validPropertyList replaceObjectAtIndex:i withObject:dict];
NSLog(#"new value in array when should be no is: %#",[validPropertyList objectAtIndex:i]);
}
}
[tv reloadData];
}
This does set all of the values in my array to the correct values, and then I call the [tv reloadData] to update the table. It doesn't visually change the values of the checkboxes though. That's my problem.
I've tried adding checkboxes to my dictionary instead of NSNumbers, but no difference. I've tried binding the checkboxes in the table to the bool selectAllValue, but no difference.
I've read other posts on SO that relate to checkboxes in tables, but none of them really address this. Again, any help is greatly appreciated.
Please let me know if you need more information.
Thanks
Scott

NSRuleEditor: Criteria for new row

When I hit the "+" button on a row in NSRuleEditor, a new row is created. How can I take influence on the criteria used for that row.
It seems NSRuleEditor defaults to selecting the first criterion sequentially from the list of possible values. I would much rather have the new row match the row where the "+" was clicked.
I was able to fake it by subclassing a private method:
- (void)_addOptionFromSlice:(id)slice ofRowType:(unsigned int)type
{
int rowIndex = [(NSRuleEditorViewSlice*)slice rowIndex];
NSArray *criteriaForRow = [self criteriaForRow:rowIndex];
NSArray *displayValuesForRow = [self displayValuesForRow:rowIndex];
self.template = [NSArray arrayWithObjects:criteriaForRow, displayValuesForRow, nil];
[super _addOptionFromSlice:slice ofRowType:type];
}
- (void)insertRowAtIndex:(NSInteger)rowIndex withType:(NSRuleEditorRowType)rowType asSubrowOfRow:(NSInteger)parentRow animate:(BOOL)shouldAnimate
{
[super insertRowAtIndex:rowIndex withType:rowType asSubrowOfRow:parentRow animate:shouldAnimate];
NSArray *template = self.template;
if (template != nil) {
[self setCriteria:[template objectAtIndex:0] andDisplayValues:[template objectAtIndex:1] forRowAtIndex:rowIndex];
}
}

Resources