Updating Checkboxes In NSTableView With A "Select All" Checkbox - cocoa

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

Related

xcode Converting UITableView to UICollectionView (no valid cell)

EDIT: I should specify that this only happens when I attempt to use the UICollectionViewFlowLayout, not when I try to use a custom view. But either way nothing ever shows up on the CollectionView though it was working just fine before I converted from a TableView.)
So I've been trying to convert a UITableView that I had into a UICollectionView. So far so good. But when I try to run the app it gives me the error:
'the collection view's data source did not return a valid cell from -collectionView:cellForItemAtIndexPath: for index path {length = 2, path = 0 - 0}'
I checked all the similar questions and answers here... so in my viewDidLoad I have (tableView is actually a UICollectionView):
UINib * placeCell = [UINib nibWithNibName:#"Shops" bundle:nil];
[self.tableView registerNib:placeCell
forCellWithReuseIdentifier:CellIdentifier];
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UICollectionView *)tableView numberOfItemsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [_entries count];
//return 5;
}
- (void)tableView:(UICollectionView *)tableView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.item == [_entries count]-1 && page > 1) {
NSLog(#"load more");
//add footer view loading
if (c_page == page) {
// _tableView.tableFooterView = nil;
}
else
{
c_page++;
[self loadPlace:c_page];
}
}
}
- (UICollectionViewCell *)tableView:(UICollectionView *)tableView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
PlaceCell *cell = (PlaceCell *)[tableView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
UINib * placeCell = [UINib nibWithNibName:#"Shops" bundle:nil];
//cell = [cellLoader instantiateWithOwner:self options:nil];
NSArray *topLevelItems = [placeCell instantiateWithOwner:self options:nil];
cell = [topLevelItems objectAtIndex:0];
Place *p = [_entries objectAtIndex:indexPath.row];
cell.placeName.text = p.PName;
NSLog (#"p:%#", p.PName")
cell.placeImg.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:p.PImage]]];
return cell;
}
I went into the xib of the UICollectionViewCell (PlaceCell) and made sure that "Cell" was the reuseidentifier. And I made sure that the datasource and delegate were connected to file's owner in the collectionView.
I also noticed that when I use a custom layout instead of the flow layout (like this one: https://github.com/ShadoFlameX/PhotoCollectionView/blob/master/CollectionViewTutorial/BHPhotoAlbumLayout.m ) it doesn't give me that error... but my collectionview still isn't populated.
So I'm wondering if there's some sort of log I can run or something I can do to figure out what's going wrong. Because I've tried all the solutions I've seen and it hasn't gotten me anywhere.
When you make a cell in a xib file you should register the xib, not the class. Also, when you register either the class or xib (or make the cell in the storyboard), you don't need an if (cell==nil) clause because your cell will never be nil when you dequeue it with dequeueReusableCellWithReuseIdentifier:forIndexPath:. You should delete that clause.
So the problem is: "Switched from UITableView to UICollectionView and no valid cell is being returned." It is really a two part answer. The crux of which is every instance of UITableView...
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 50, self.view.bounds.size.width, self.view.bounds.size.height-50)];
...you want to turn into "CollectionView"
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 50, self.view.bounds.size.width, self.view.bounds.size.height-50)];
Everything that's a "row":
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
...you'll want to turn into an "item."
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
Ultimately I had to delete the following section entirely:
UINib * placeCell = [UINib nibWithNibName:#"Shops" bundle:nil];
//cell = [cellLoader instantiateWithOwner:self options:nil];
NSArray *topLevelItems = [placeCell instantiateWithOwner:self options:nil];
cell = [topLevelItems objectAtIndex:0];
My best guess is that the Nib was being loaded twice and that Xcode was complaining that the data wasn't being loaded by the original. So getting rid of that second entry got my cells loaded and populated with data. Hope this helps someone.

How to Reorder Cells Between Sections with NSFetchedResultsController

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?

Using an array of arrays to populate NSTableView

I currently have a number of arrays, each containing show title, description and duration. I have them in a further 'shows' array and I'm using this array to populate my NSTableView. What I would like to do is extract the show title from each of my arrays for the first column of my table, the description for the second and so on.
The code I have at the moment though takes the first array in my array of arrays and populates column one, the second array for the second column etc. How would I amend what I have so far to get the table to populate correctly? I've tried to use indexOfObject in place of objectAtIndex however doing so throws and exception. Here's my (simplified) code:
AppDelegate.m
- (void)applicationDidFinishLoading:(NSNotification *)aNotification
{
NSArray *show1 = [[NSArray alloc] initWithObjects:#"Title", #"A description", nil];
NSArray *show2...
NSArray *show3...
NSArray *show4...
self.array = [[NSMutableArray alloc] initWithObjects: show1, show2, show3, show4, nil];
[self.tableView reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [self.array count];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSString *identifier = [tableColumn identifier];
if([identifier isEqualToString:#"title"]) {
NSTableCellView *title = [tableView makeViewWithIdentifier:#"title" owner:self];
title.textField.stringValue = [self.array objectAtIndex:0];
return title;
} else if {...}
return nil;
}
Michele Percich's comment is the correct answer: [self.array objectAtIndex:0] will return the first shows array. What you want is "NSArray * show = [self.array objectAtIndex:row]'" to get the show and then "[show objectAtIndex:0]" to get that shows title. Just a suggestion but I'd use an NSArray of NSDictionary's where the keys are the column identifiers. Then you could just use "[self.array objectAtIndex:row] valueForKey:identifier];"
Note also that the method you're overriding expects an instance of NSView (or subclass) to be returned (read the notes in the NSTableView.h header). You may want to use the tableView:objectValueForTableColumn:row: method instead and just return the appropriate NSString (based on the row & column identifier).

<NSInternalInconsistencyException> Invalid update: invalid number of sections

I am adding user defined text in a UItableView via a modal view controller.
The table is created as a NSMutableArray. on entering data in the modal view and tapping a Done button a Invalid update: invalid number of sections. error is generated.
I must update the array when the user enters data but not sure how to do that. I have attached the relevant code. Thanks for your advice.
//Number of sections
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }
//Number of rows
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [listOfConjProcedures count];
}
//Loading the cells
- (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];
}
// Configure the cell.
cell.textLabel.text = [listOfConjProcedures objectAtIndex:indexPath.row];
return cell;
}
//Done button Delegate for the modal view
- (void)itemDetailViewController:(ItemDetailViewController *)controller didFinishAddingItem:(ChecklistItem *)item
{
int newRowIndex = [listOfConjProcedures indexOfObject:item];
[listOfConjProcedures addObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newRowIndex inSection:0];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[self dismissViewControllerAnimated:YES completion:nil];
}
I think your problem may be here:
int newRowIndex = [listOfConjProcedures indexOfObject:item];
This wouldn't have an index unless you'd already added it to the array, but in the next line you add it to the array, so it doesn't make sense.
I think you are therefore adding a row at index path 0,NSNotFound, which will confuse the table view. Either use the count of the array as the row index, or insert your new row at the start (0,0) of the table (and at index 0 of the array).

Issue with UITableView - updating table values

I am a newbie. I have written some code for UITable but I am unable to update table values after adding the values to the array. I'm using a tableview subclass. The code is as follows. Check the last function. Now how can I update my table values?
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return [_phones count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
HLPFoundPhones *p = (HLPFoundPhones *)[_phones objectAtIndex:indexPath.row];
NSString *subtitle = [NSString stringWithFormat:#"Found (%1.2f,%1.2f) on %#" ,[p loc].x, [p loc].y , [p foundDate]];
cell.textLabel.text = [p name];
cell.detailTextLabel.text = subtitle;
return cell;
}
- (void)insertNewObject
{
HLPPhonesAdd *view = [[HLPPhonesAdd alloc]initWithNibName:#"HLPPhonesAdd" bundle:nil];
[self.navigationController pushViewController:view animated:YES];
[view release];
}
- (void)updateTable:(CGPoint)loc name:(NSString *)_name{
HLPFoundPhones *a = [[HLPFoundPhones alloc]initWithLoc:loc name:_name];
[_phones addObject:a];
[phones reloadData];
}
call
[yourTableView reloadData];
in your updateTable Function..
- (void)updateTable:(CGPoint)loc name:(NSString *)_name{
HLPFoundPhones *a = [[HLPFoundPhones alloc]initWithLoc:loc name:_name];
[_phones addObject:a];
//give your tableView object instead of yourTableView
[yourTableView reloadData];
}
check value of _phone count and use in function
- (void)updateTable:(CGPoint)loc name:(NSString *)_name
{
HLPFoundPhones *a = [[HLPFoundPhones alloc]initWithLoc:loc name:_name];
[_phones addObject:a];
[table_name reloaddata];
}
Confirm the array _phones is allocated.
Then there will be problems if the [tableview reloadData] is called inside a thread.
So confirm the reloadData is called from the main thread.
First of all, your cell identifier looks to be same for all cells, which may confuse iOS which cell needs to be dequeued. So, keep your cell identifiers unique, something like below :
NSString *cellIdentifier = [NSString stringWithFormat:#"Cell-%d", indexPath.row];
then, after table view is first displayed, update your datasource and call:
[tableView reloadData];

Resources