Why can't I place my NSDictionary data into a UITableView - ios

I am have an NSDictionary which multiple dictionaries like this:
{
complete = 0;
description = Description;
"due_date" = "2014-02-28 16:30:03";
name = Task;
priority = 2;
"task_id" = 1;
"user_id" = 1;
},
{
complete = 0;
description = "";
"due_date" = "0000-00-00 00:00:00";
name = "";
priority = 0;
"task_id" = 2;
"user_id" = 1;
}
I would like to display each instance of "name" in my UITableView
I have tried this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
for (task in tasks) {
cell.textLabel.text = [[task objectAtIndex:indexPath.row] valueForKey:#"name"];
}
return cell;
}
But the app crashes each time I attempt to do run it.
What am I doing wrong?
Here is the error I get:
Assertion failure in -[UITableView _configureCellForDisplay:forIndexPath:]

Change your code to look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
cell.textLabel.text = [[tasks objectAtIndex:indexPath.row] valueForKey:#"name"];
return cell;
}
The problem was that task was a dictionary, so it didn't respond to objectAtIndex:.
BTW, you need to create or dequeue a UITableViewCell instance as well, but I simply corrected your crashing issue for you.

The reason you are getting a crash is because you are returning nil from tableView:cellForRowAtIndexPath:.
I think you are also misunderstanding how this table view data source method works. It gets called once per table cell in your table (hence why you return a UITableViewCell from it...). You don't need to be looping over your data collection inside it - you should be getting the correct data object from your collection based on the indexPath.
On top of that, as others have said, if tasks is in fact an NSDictionary, then it won't respond to objectAtIndex. In all likelihood, you probably want your data collection to be an NSArray, if it isn't already.
Edit:
Judging by the error you are getting, tasks is in fact an NSArray (as the error is caused by returning nil from tableView:cellForRowAtIndexPath:). Try the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellIdentifier" forIndexPath:indexPath];
NSDictionary *task = [tasks objectAtIndex:indexPath.row];
cell.textLabel.text = [task objectForKey:#"name"];
return cell;
}
Edit 2:
Also, you should be using objectForKey: instead of valueForKey:.

Dictionaries don't have indices.
You can't call a objectAtIndex: method on a dictionary, that's why your app is crashing. Instead, you need to use objectForKey: with the appropriate key to get the value you want.

Try storing those NSDictionary's in an NSArray. An NSArray will allow you to hold them, along with accessing objects with "objectAtIndex".
If you need to add NSDictionary's at run-time, try using an NSMutableArray, which allows you to add and remove array objects when needed.

Related

If statement to display cell

my question is, if it is possible to display a cell only if there is data present for a specific post. I'm thinking something along the line of this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
PFQuery *query = [PFQuery queryWithClassName:#"Post"];
if(![query includeKey:#"Photo"]) {
UITableViewCell *cell = [self tableView:tableView cellForNextPageAtIndexPath:indexPath];
return cell;
}
static NSString *CellIdentifier = #"PhotoCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
PFImageView *photo = (PFImageView *)[cell viewWithTag:1];
photo.file = object[#"image"];
[photo loadInBackground];
return cell;
}
I've tried this but i can not get it to work every which way try, any help will be apreciated, thank you in advanced.
Tables need a model, and that model is almost always an array. The datasource protocol's job is to ask about that array.
So what's in your array? Sometimes nothing, because you haven't fetched the data for it yet, or because the user has no data that belongs there. From your question, it sounds like sometimes your array has 1 element -- something representing a post or an image -- and sometimes your array has 2 elements -- something representing a post and an image.
Thinking this way lets you divide your work into two parts: (1) do a fetch and create an array that represents the current state, (2) implement a table that presents the state of that array to the user.
Lets start with the array (note this is pseudo-code, meant to illustrate the idea):
// in interface
#property(strong,nonatomic) NSMutableArray *model;
// in init
_model = [NSMutableArray array];
// sometime at or after viewDidAppear, update your model by querying parse
// I don't understand your parse data model, but you want some query
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// based on the parse objects returned, initialize model
[self.model addObject: ...];
// tell your table that the model has changed
[self.tableView reloadData];
}
}];
With your array handled elsewhere, the datasource job is simpler:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.model.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// dequeue a cell, configure its subviews based on self.model[indexPath.row]
// no parse code here. aim to configure the cell strictly based on the model
return cell;
}
Notice how the table view is ignorant of parse.com and the parse code is largely ignorant of the table view.

Blank rows on 2 TableViewController with data from one Viewcontroller in the other viewcontroller

I have 2 UITableViewControllers in my project.
The problem I am having is that I am getting blank cell entries in the tableView opposite to the tableView where the data is entered.
I can't seem to figure out why this is the case.
It's creating blank rows in this tableView even though the information is from the other UITableViewController.
Here's the main tableView part from the one of the 2 UITableViewControllers:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"number of addedSpaceObjects %lu",(unsigned long)[self.diaryoptions count]);
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"number of sections %ld",(long)section);
// Return the number of rows in the section.
return [self.diaryoptions count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentification = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentification
forIndexPath:indexPath];
Data2 *diary = [self.diaryoptions objectAtIndex:indexPath.row];
cell.textLabel.text = diary.diaryname;
cell.detailTextLabel.text = diary.diaryWeight;
return cell;
}
And from other UITableViewController:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"number of addedSpaceObjects %lu",(unsigned long)[self.addedSpaceObjects count]);
// Return the number of sections.
if ([self.addedSpaceObjects count]) {
return 2;
}
else {
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"number of sections %ld",(long)section);
// Return the number of rows in the section.
if (section == 1) {
return [self.addedSpaceObjects count];
}
else {
return [self.recipes count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentification = #"Josh";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentification
forIndexPath:indexPath];
if (indexPath.section == 1) {
Data *recipe = [self.addedSpaceObjects objectAtIndex:indexPath.row];
cell.textLabel.text = recipe.name;
}
else {
// Configure the cell...
Data *recipe = [self.recipes objectAtIndex:indexPath.row];
cell.textLabel.text = recipe.name;
}
return cell;
}
Here is the full project on GitHub. https://github.com/josher32/Plant-Diet
Appreciate any help anyone can offer!
Ok, so I checked out the app and I'll try my best to explain the problem as precisely as I can to cover it adequately.
Firstly, the classes in question are:
RecipesTableTableViewController
AddRecipeViewController
Data
DiaryTableViewController
AddDiaryViewController
Data2
Secondly, we'll need to look into your
#define ADDED_SPACE_OBJECTS2 #"Added Space Objects Array"
AddRecipeViewController
So... AddRecipeViewController basically creates a Data object that is kept in an array and eventually stored in NSUserDefaults under the key name Added Space Objects Array.
Great!! So you now have got recipe related stuff in some Data object.
AddDiaryViewController
Same thing here.
AddDiaryViewController creates a Data2 object that is eventually stored in NSUserDefaults under the same key name Added Space Objects Array.
But before storing this, you're taking the old value of the key Added Space Objects Array, which is an array, and adding a new object to it before placing it back into NSUserDefaults.
But now... this array will now have a combination of Data as well as Data2 objects!
RecipesTableTableViewController
When we come here, things get real.
- (void)viewDidLoad
{
//...
NSArray *myRecipeAsPropertyLists = [[NSUserDefaults standardUserDefaults] arrayForKey:ADDED_SPACE_OBJECTS_KEY];
for (NSDictionary *dictionary in myRecipeAsPropertyLists) {
Data *spaceObject = [self spaceObjectForDictionary:dictionary];
[self.addedSpaceObjects addObject:spaceObject];
}
}
Since we already realized that self.addedSpaceObjects can contain Data as well as Data2 objects, in the case whendictionary is containing stuff specific to type Data2, spaceObjectForDictionary will not be able to translate it properly to the required Data object.
We're expecting name, title, ingredients, directions but we're getting diaryentry, diaryname,diaryWeight.
So (in this scenario):
The values of name, title, ingredients, directions will be nil
The section-row count will be incorrect because it will give count of both Data as well as Data2 objects (and we don't care about Data2 objects in the RecipesTableTableViewController class... right?... well anyways, I assumed)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
if (indexPath.section == 1) {
Data *recipe = [self.addedSpaceObjects objectAtIndex:indexPath.row];
cell.textLabel.text = recipe.name;
}
//...
}
We see recipe.name is nil, for some indexPaths, ergo blank rows and vice versa in DiaryTableViewController.
Solution:
Firstly, I wouldn't recommend NSUserDefaults for your purposes but anyways...
Basically, don't use a single #"Added Space Objects Array" key for your NSUserDefaults stuff.
I'd suggest you use 2 separate keys.
//replace
//#define ADDED_SPACE_OBJECTS2 #"Added Space Objects Array"
//with
#define ADDED_SPACE_OBJECTS2 #"RecipeEntries" //in RecipesTableTableViewController
//and
#define ADDED_SPACE_OBJECTS2 #"DiaryEntries" //in DiaryTableViewController
Basically, segregate the entries instead of mixing them up under a single key name.
This seems like the quickest way to solve your problem without changing your logic.

Get text from one label on multi-label UITableViewCell

On my UITableViewI am using custom UITableViewCells. Each of these cells has a number of labels. When the user selects a cell, I need to capture the contents of just one of these labels, but I don't know how to do that. Here is my code. The line that I am using to attempt to get this label text is basically pseudo-code that clearly won't compile. Can somebody please tell me what I need to do here? Thanks!
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
Groups *group = [self.fetchedObjects objectAtIndex:indexPath.row];
cell.groupDescriptionLabel.text = group.group_descr;
cell.groupIDLabel.text = [group.group_id stringValue];
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// capture the user selection
Groups *group = [self.fetchedObjects objectAtIndex:indexPath.row];
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *selection = selectedCell.groupDescriptionLabel.text; //<-- pseudo-code
NSLog(#"%#", group.group_descr);
...
}
It's generally a bad idea to get data out of the view. You shouldn't really be using any view objects as a way of storing information.
You are already getting the string when you are in the cellForRowAtIndexPath method.
You should be able to do the same in didSelectRowAtIndexPath to get the same string.
That way you don't have to get the text out of the label at all.
Thanks
If you know that celForRowAtIndexPath is going to be returning one of your custom cell types instead of a generic UITableViewCell, cast the result to your custom cell class:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// capture the user selection
MyCellClass *selectedCell = (MyCellClass *) [tableView cellForRowAtIndexPath:indexPath];
NSString *selection = selectedCell.groupDescriptionLabel.text; //<-- pseudo-code
NSLog(#"%#", selection);
//...
}

iOS: When is cellForRowAtIndexPath called? Or do tags get updated?

I'm starting to get confused. I'm using a FetchedResultsController for my tableview data. In each cell I have a button and a textfield tagged with the indexPath.Row in the cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Data model and cell setup
static NSString *CellIdentifier = #"MainCategoryCell";
MainCategoryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
MainCategory *mainCategory = [self.fetchedResultsController objectAtIndexPath:indexPath];
/* ... */
cell.title.tag = indexPath.row;
cell.iconButton.tag = indexPath.row;
return cell;
}
Now my row move method is a bit more complicated for the Fetched Results controller. However I'm pretty sure the tags don't get updated after the moving. Is that normal and is the cellForRow method only called after creating a new cell? Do I have to update the tags myself in the move method? And how could I access there the tag properties of the objects within the cells?
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toIndexPath:(NSIndexPath *)destinationIndexPath;
{
// Process the row move. This means updating the data model to correct the item indices.
//reordering has been defined in the CoreDataViewController so the
//FetchedResultsController doesn't mess up the reordering since he would update
//the fetched results permanently while reordering
self.reordering = YES;
//Makes only a mutable copy of the array, but NOT the objects (references) within
NSMutableArray *fetchedResults = [[self.fetchedResultsController fetchedObjects] mutableCopy];
// Grab the item we're moving
NSManagedObject *resultToMove = [self.fetchedResultsController objectAtIndexPath:sourceIndexPath];
// Remove the object we're moving from the array.
[fetchedResults removeObject:resultToMove];
// Now re-insert it at the destination.
[fetchedResults insertObject:resultToMove 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 = 1;
for (MainCategory *fetchedResult in fetchedResults)
{
fetchedResult.position = [NSNumber numberWithInt:i++];
}
// Save
NSError *error = nil;
[self.budgetDatabase.managedObjectContext save:&error];
// re-do the fetch so that the underlying cache of objects will be sorted
// correctly
[self.fetchedResultsController performFetch:&error];
self.reordering = NO;
}
Yes it is normal that the tags won't get updated when you move cells. Since all cells have the possibility of being shifted, simply reload the table view to have it regenerate the tags for your button and text box.
[tableView reloadData];

How to skip a specific string when populating my UITableView

I am populating a tableview from data that is received from a server. The data is a list of user activities within a given timeframe. One such activity is "Login". I do not wish to populate my tableview with this string but I'm not sure how to skip it when populating my tableview.
Here is how I populate the cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
#try{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *action = [object valueForKey:#"theActionName"];
if ([action isEqualtoString:#"Login"]) {
return cell;
}
return cell;
}#catch (NSException *ex) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
}
As you can see I tried using return cell but as you probably know it gives me a blank cell when the table is displayed. I'm sure there is a simple line of code for this but I came up blank with the search terms I used. Could someone please enlighten me! Thanks!
P.S. you may be thinking I am not putting anything in any of the cells but I pulled out a bunch of code to keep this short.
UPDATE:
Thanks for the heads up on "isEqualtoString:" Everything worked fine with "isEqual" but I changed it given that I received so many suggestions to do so. But this is not what I am asking.
To be more clear if I had an array containing the terms: view, view, login, view. When my tableview was populated I would have 4 cells that said; view, view, login, view. I simply want to ignore the term login so that I would have 3 cells that all said view. Thanks!
There can be many way to do this.
I Belive that UITabelView should display what its datasource (here datasource is self.fetchedResultsController) contains.
What you can do is create another NSArray from self.fetchedResultsController which does not contain this object.
Try this:
NSMutableArray *newSource = [[NSMutableArray alloc] init];
for(int i = 0; i < self.fetchedResultsController.count ; i++)
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *action = [object valueForKey:#"theActionName"];
if (![action isEqual:#"Login"])
{
[newSource addObject:action];
}
}
[tableView reloadData];
Now use newSource instead of self.fetchedResultsController
You might think that using one more array is not good. But believe it it is far easier than using the same array with condition. You don't have to worry about that condition when you perform some operation with your UITableView like remove object by using indexpath.
try using if ([action isEqualToString:#"Login"])
When you want to compare strings you need to use this isEqualToString .
Change this line
if ([action isEqualToString:#"Login"]) {
return cell;
}
You are using the wrong function to compare your input string and the given data variable.
They both are NSString objects so use :
if([action isEqualToString:#"Login"])
{
//enter your code here
}
#Ben : I am assuming that you have registered you cell through nib as you are using dequeueReusableCellWithIdentifier.
Make your tableview content as "Dynamic prototype" (You can see this in Attributes Inspector of table view) and change your table view cell style as custom (You can see this in Attributes Inspector of tableview cell).

Resources