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.
Related
when I fetch the record from core data it returns null value...i don't know what is the problem...
When I check in NSLog it shows like this:
"<Ad: 0x979fb30> (entity: Ad; id: 0x9799470 <x-coredata://B3AA111F-8307-4A16-B898-
403A804DFDFB/Ad/p22> ; data: <fault>)",
"<Ad: 0x979fd70> (entity: Ad; id: 0x9799480 <x-coredata://B3AA111F-8307-4A16-B898-
403A804DFDFB/Ad/p23> ; data: <fault>)
Here is my code.
I returned all the core data functions in separate class called dbmanager.
- (void)viewDidLoad
{
DBManager *manager=[[DBManager alloc]init];
self.fetchedRecordsArray = [manager fetchAllAds];
[self.shoppingtbl reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.fetchedRecordsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *CellIdentifier = #"ShoppingCart";
ShoppingCart *cell = (ShoppingCart*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
cell.backgroundColor=[UIColor clearColor];
ad=[self.fetchedRecordsArray objectAtIndex:indexPath.row]
cell.addesc.text = ad.data;
cell.adId.text=[NSString stringWithFormat:#"%lu", (long)[ad.adId integerValue]];
cell.adstatus.text=[NSString stringWithFormat:#"%#",ad.state];
return cell;
}
The entity name called Ad. It contains Adid,state,data.
In table view it shows null value for strings and 0 for numbers..
I recommend you abandon your fetchedRecordsArray and embrace the NSFetchedResultsController. It is specifically designed to work with core data and table views.
This will almost certainly eliminate the problem you have and result in much more robust and scalable code. You could be displaying 100.000s ads this way without any problem.
One more hint, i.e. something you might want to check beforehand: quite possibly you did not save the data correctly. It looks like you saved each record with [managedObjectContext save:&error] after it was created, but perhaps you did not do this correctly after populating it with state, data. The way to check this is to enumerate your array elements and NSLog those bits of information to check if they are there.
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.
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];
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).
I´m quite new to iOS development and I´m having a terrible time by trying something that should be easy; to add an extra row in a TableView everytime the user clicks on one of the existing rows. There is no real purpose on that action, I´m just wanting to understand the behaviour of TableView.
So I did the following:
I used a Split View-based template and changed the number of rows to 30 in the RootViewController.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 30;
}
The method tableView:didSelectRowAtIndexPath looks in the following manner:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
/*
When a row is selected, set the detail view controller's detail item to the item associated with the selected row.
*/
NSMutableArray* paths = [[NSMutableArray alloc] init];
NSIndexPath *indice = [NSIndexPath indexPathForRow:30 inSection:0];
[paths addObject:indice];
detailViewController.detailItem = [NSString stringWithFormat:#"Second Story Element %d with all its information and bla bla bla", indexPath.row];
[[self tableView] beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
}
When I execute the program and click on one of the elements, I receive the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (30) must be equal to the number of rows contained in that section before the update (30), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted).'
I did not change any other part of the code that the template provides.
I read quite extensively the documentation from Apple and the responses to the following questions:
Add a row dynamically in TableView of iphone
and
how to properly use insertRowsAtIndexPaths?
The second question seems to address the same problem, but I´m not capable to understand what is happening. What do they mean with dataSource? The response that I understand better says the following:
It's a two step process:
First update your data source so numberOfRowsInSection and cellForRowAtIndexPath will return the correct values for your post-insert data. You must do this before you insert or delete rows or you will see the "invalid number of rows" error that you're getting.
What does this update of the data source implies?
Sample code would be HIGHLY appreciated, because I´m totally frustrated.
By the way, all that I´m trying has nothing to do with entering the editing mode, has it?
You need to keep the count returned by tableView:numberOfRowsInSection: in sync!
So when you have 30 rows and then tell the tableview to insert a new row you need to make sure tableView:numberOfRowsInSection: will now return 31.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section
{
return self.rowCount;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.rowCount++;
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
In practice you would probably use an array to track your rows return [self.rows count]; etc
The answer is quite simple. When you want to modify a table view you need to perform two simple steps:
Deal with the model
Deal with table animation
You already perform the second step. But you have missed the first one. Usually when you deal with a table you pass it a data source. In other words some data to display within it.
A simple example is using a NSMutableArray (it's dynamic as the name suggests) that contains dummy data.
For example, create a property like the following in .h
#property (nonatomic, strong) NSMutableArray* myDataSource;
and in .m synthesize it as:
#synthesize myDataSource;
Now, you can alloc-init that array and populate it as the following (for example in viewDidLoad method of your controller).
self.myDataSource = [[NSMutableArray alloc] init];
[self.myDataSource addObject:#"First"];
[self.myDataSource addObject:#"Second"];
Then, instead of hardcoding the number of rows you will display (30 in your case), you can do the following:
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
return [self.myDataSource count];
}
Now, in you didSelectRowAtIndexPath delegate you can add a third element.
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.myDataSource addObject:#"Third"];
[[self tableView] beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
}
It looks like one big problem is with tableView:numberOfRowsInSection:. You need to return the correct number of rows in that method.
To do that, it's usually best to maintain an NSArray or NSMutableArray of items for the table view so in that function, you can say: return [arrayOfValues count];. Keep the array as a property of your view controller class so that it's readily accessible in all methods.
The array can also be used in cellForRowAtIndexPath:. If you have an array of NSString, you can say cell.text = [arrayOfValues objectAtRow:indexPath.row];.
Then, when you want to add an item to the table view, you can just add it to the array and reload the table, e.g. [tableView reloadData];.
Try implementing this concept and let me know how it goes.
You can Also do that for dayanamic table cell
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayStationStore count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIndentyfire;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentyfire];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentyfire];
}
cell.textLabel.text = [arrayStationStore objectAtIndex:indexPath.row];
return cell;
}
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Check if current row is selected
BOOL isSelected = NO;
if([tblStationName cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark)
{
isSelected = YES;
}
if(isSelected)
{
[tblStationName cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
[arrayReplace removeObject:indexPath];
NSLog(#"array replace remove is %# ",arrayReplace);
}
else
{
[tblStationName cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
[arrayReplace addObject:indexPath];
NSLog(#"array replace add is %# ",arrayReplace);
}
return indexPath;
}