Populating UITableView with Dictionary Data - iphone

My Situation:
I have an NSDictionary object. Keyed by NSNumber. Values are custom objects.
I want to put dictionary values into a UITableView. As far as I can tell, UITableView requires that its source collection be indexed so when cellForRowAtIndexPath is called, you can use the indexPath to look up the value.
Problem is that when didSelectRowAtIndexPath is called, I want to look up the object from the dictionary, but I don't have the key. All I have is the indexPath.row.
My solution:
I create an array of keys. I use the index of the array to get the key, and then use the key to get the object out of the dictionary.
My problem:
This seems kind of sloppy especially since this is a routine task (populating the UITableView and then responding when someone touches a cell). Is this the way it's designed to work or is there a better way?

The problem is that dictionaries don't have an order, while a table view does. The answers to this question should give you some ideas for alternative ways of handling this.

As mentioned in another answer, an NSDictionary's keys are not ordered, therefore you are not guaranteed to get the rows in a particular order. That said, it is quite easy to use a dictionary for use with a UITableView.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// tableview cell setup
NSArray* keys = [self.data allKeys];
cell.textLabel.text = [self.data objectForKey:[keys objectAtIndex:indexPath.row]];
return cell;
}
If you need the list to be ordered according to how you entered them into the NSDictionary, Matt Gallagher from Cocoa With Love offers an elegant solution with his take on OrderedDictionary. You can read about it here.

Related

How can I update the coredata records after rearranged the tableview cells

I know this question has been asked before, However, I'm still confused as to how to implement reordering with a UITableView cells in a Core Data project.Below mentioned code i have used in my project for rearranged the TableView cells. After rearranged cells not affected in core data.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
NSInteger sourceRow = fromIndexPath.row;
NSInteger destRow = toIndexPath.row;
Question *cat1=[questionsArray objectAtIndex:sourceRow];
[questionsArray removeObjectAtIndex:sourceRow];
[questionsArray insertObject:cat1 atIndex:destRow];
[self.cardsTable setEditing:NO animated: YES];
[cardsTable reloadData];
}
If you can target iOS 5.0 and above, then you could use an NSOrderedSet to maintain the order of your objects. Please keep in mind that using this method is significantly less efficient than the other method I am suggesting below (as per Apple's documentation). For more information, please check the Core Data Release Notes for iOS 5.
If you need to support iOS versions before 5.0 or would like to use a much more efficient approach, then you should create an additional integer attribute in your entity and manually maintain the index of the entity's objects in it as they are being added or rearranged. When it's time to display the objects, you should sort them based on this new attribute and you're all set. For example, this is how your moveRowAtIndexPath method should then look like:
- (void)moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath sortProperty:(NSString*)sortProperty
{
NSMutableArray *allFRCObjects = [[self.fetchedResultsController fetchedObjects] mutableCopy];
NSManagedObject *sourceObject = [self.fetchedResultsController objectAtIndexPath:sourceIndexPath];
// Remove the object we're moving from the array.
[allFRCObjects removeObject:sourceObject];
// Now re-insert it at the destination.
[allFRCObjects insertObject:sourceObject atIndex:[destinationIndexPath row]];
// Now update all the orderAttribute values for all objects
// (this could be much more optimized, but I left it like this for simplicity)
int i = 0;
for (NSManagedObject *mo in allFRCObjects)
{
// orderAttribute is the integer attribute where you store the order
[mo setValue:[NSNumber numberWithInt:i++] forKey:#"orderAttribute"];
}
}
Finally, if you find this too much manual work, then I really recommend using the free Sensible TableView framework. The framework will not only maintain the order automatically for you, it will also generate all the table view cells based on your entities' attributes and its relationships with other entities. Definitely a great time saver in my opinion. I also know of another library called UIOrderedTableView, but I've never used that myself so I can't recommend it (the former framework is also much more popular).
First you have to keep the index in to your NSManagedObject.
When you in insert a new object make sure you set the index to be lastIndex+1
After you fetch for the objects sort by your index.
When you reorder the cells you also have to set the indexes to the objects. Pay attention because you might have to change the indexes to all objects.
Example: You take the first cell and move it to last position. In this case all your indexes are changed. The FirstCell takes last index and all others take oldIndex-1.
I sugest after done editing iterate to your data source array and just set the object index to the iteration index.

Does the UITableView function visibleCells return custom cell data?

I have a number of custom cell objects (subclasses of UITableViewCell) with a couple of values in them to allow for user interaction within individual cells (like steppers or something). These values are stored within the custom cell class, since calling up to the owner of the table view seemed like a bad idea at the time.
I know of the function (NSArray *) visibleCells. Will that allow me to access the data within the cell objects?
If not, how?
I'm assuming that I can use the built-in functions of the UITableView to pull returned UITableViewCells, but is that sufficent when I'm talking about a subclass of that called, say CustomizerCell?
The function:
- (NSArray *)indexPathsForVisibleRows
Answers an array of index paths. Those index paths can be used the same way your cellForRowAtIndexPath uses the passed index path to access your model.
MyObject *myCustomDataSupportingACell = [myDatasourceArray objectAtIndex:indexPath.row];

How can I centralize cellForRowAtIndexPath?

I'm looking to have a single - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath method that all UITableView's will use for formatting, etc.
What is the best way to accomplish this?
Right now I'm thinking I could use a separate class or put delegate methods in my app delegate and hook the UITableView's file's owner to that. But then I'd have to provide those classes/methods access to the data that would normally otherwise be right in the ViewController subclass.
How have others accomplished this sort of thing?
EDIT: To clarify, I'm not looking for a way to return a common cell itself, but rather a way to have a single cellForRowAtIndexPath method in my entire app.
If you just want to centralize all of the formatting but have the data sources separate in each class you could create a factory class for your table view cells which provides a method
+ (UITableViewCell* ) tableViewCellForTableView:(UITableView *)tableView withType:(int)type;
By creating type costants which get passed in, you can create a set of basic table view cells that can then be returned by this class. It is important to pass in the tableView to make use of reusable cells. A cellForRowAtIndexPath could now fetch cells from there and configure them depending on the data
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [MyCellFactory tableViewCellForTableView:tableView withType:kMyTypePlain];
cell.textLabel.text = ....;
}
You may consider using an objective-c category on your ViewController which implements the cellForRowAtIndexPath method.
If you're doing something very complex in this method however, I suggest you just create a nib file which contains your cell and then load it in cellFoRowAtIndexPath. You could even create a class that inherits from UITableViewCell and stick your own methods/properties in there. I highly recommend this approach.
You can load the nib, say "MyCustomTableCell.nib" which contains your customised UITableViewCell like so:
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyCustomTableCell"
owner:self
options:nil];
MyCustomTableCell* cell = [topLevelObjects objectAtIndex:0];
I think you can use the answer from #TriPhoenix for most of this.
The only additional thing I would add is that you could potentially send the data along with the call to the factory.
[MyCellFactory cellForTableView:tableView withType:kMyTypePlain data:data];
You would of course have to ensure that the data responds to a common interface to ensure that you can set things easily e.g. making sure each object responds to a similar method like
cell.textLabel.text = data.textLabelText;
You could do this using a protocol if you like. It's up to you how you structure your data.

Alternatives to NSMutableDictionary?

I have 3 NSMutableDictionaries that hold data for 3 separate UITableViews. When a value is checked from one of the UITableViews, I want to hold that value so depending on the different values that are checked from the different tables, I can generate an answer on the next page. I thought maybe I could create a new NSMutableDictionary that has all the possible selections, and then when a user hits the checkbox, to tell my newNSMutableDictionary that that value has been selected. But I don't think it works that way since it's a key-value-pairing. I was wondering if there were alternatives to this, or if someone had a good way of holding this information? Thanks.
Instead of doing this, you can try having one NSMutableArray of the selected NSIndexPath objects.
This way, you'd have a pretty lightweight memory footprint (lazy loading) and if you needed to grab the actual cell's value, you can ask the UITableView with -tableView:cellForRowAtIndexPath:
Or, even better, have one NSMutableDictionary with one the keys being the tag of the specific tableview wrapped in an NSNumber and the value being an NSMutableArray of selected index paths.
To retrieve the selected index paths would be as simple as this:
NSArray *indexPaths = [selectedDict objectForKey:[NSNumber numberWithInt:tableView.tag]];
for(NSIndexPath *p in indexPaths) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:p];
//do something with cell...
}
why not store all possible selections in a NSDictionary, initialise it with the all the selections a user can make. When the user makes a selection add that item to a "selectedItems" NSMutableDictionary. This way your NSMutableDictionary (that contains selections) will only ever have the selected items in it, you can then add and remove from this dictionary without needing to mutate the non-mutable NSDictionary that contains the selections that a user can make..?
I'm not sure if I've understood your problem right.
But, if your data has the "shape" of a tree you might consider to save the information in this way.
You can put a Dictionary inside your Dictionary
You can put your data in CoreData and use NSTreeController

NSMutableArray to table view

i am new to the iPhone development.
i need to display the NSMutableArray contents into UITableViewCell..
it is quite simple.. but, i want to know, how to add the NSMutableArray contents into table view at runtime?
please anyone help me..
thank you very much..!
Have you read the Table View Programming Guide?
In particular the section that talks about Creating and Configuring a Table View.
The little code snippets on those guides provide examples that deal with the simplest case, which is an array.
In short, you provide a "data source", which the table view asks for each row from. That data source is usually (but doesn't have to be) the ViewController that uses the table.
Make sure to check out the example applications linked to there if you need to see some working examples in Xcode itself.
You want to add total array to single cell or a tableView. If the answer is tableView, then you can directly give the values in the following method like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
cell.textLabel.text = [array objectAtIndex: indexPath.row];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count];
}
you can use like that.