I have created an object and saved it to the backend named NewCar and there are 4 strings attached to which are year, make, model, horsepower I'm having troubles pulling the data from parse and placing it into my table here is a few pieces of my code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
PFQuery *query = [PFQuery queryWithClassName:#"NewCar"];
[query whereKey:#"year" equalTo:[[PFUser currentUser] objectId]];
[query orderByAscending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
} else {
self.cars = objects;
[self.tableView reloadData];
}
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.cars count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"garageCell";
garageCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[garageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.usernameDisplay.text = [[PFUser currentUser] objectForKey:#"username"];
//I need to display the year, make and model below here.
cell.carYearLabel.text = [[PFObject objectWithClassName:#"NewCar"] objectForKey:#"year"];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 236;
}
I am able to display the username in my custom cell, but can't figure out how to display the year, make and model values. Any help would be much appreciated.
I have been looking through the documentation in Parse and can't seem to figure it out just in case you are wondering if I tried there.
I also want to add in the code where I added the object to Parse this might help in figuring this out as well.
- (IBAction)save:(id)sender {
NSString *year = self.carYear.text;
NSString *make = self.carMake.text;
NSString *model = self.carModel.text;
NSString *horsepower = self.carHorsepower.text;
if ([year length] == 0 || [make length] == 0 || [model length] == 0 || [horsepower length] == 0) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:#"You might have missed a field" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alertView show];
} else {
PFObject *newCar = [PFObject objectWithClassName:#"NewCar"];
newCar[#"year"] = year;
newCar[#"make"] = make;
newCar[#"model"] = model;
newCar[#"horsepower"] = horsepower;
[newCar saveInBackground];
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
Okay so instead of using
cell.carYearLabel.text = [[PFObject objectWithClassName:#"NewCar"] objectForKey:#"year"];
You can use the following
cell.carYearLabel.text = [self.cars objectForKey:#"NewCar"] valueForKey:#"year"];
Related
I am attempting to work a segmented control to display data from a search into categories. However although data is received it is not being displayed on my table. Here is my code for the two View Controllers. The child View controller is the one in which the UITableView is stored.
PARENT VC
- (void)searchPeople:(NSString*)text {
if(![text isEqualToString:#""]){
PFQuery *userWithName = [PFQuery queryWithClassName:#"_User"];
[userWithName whereKey:#"fullName" containsString:text];
PFQuery *userWithHandle = [PFQuery queryWithClassName:#"_User"];
[userWithHandle whereKey:#"username" containsString:text];
PFQuery *userQuery = [PFQuery orQueryWithSubqueries:#[userWithHandle,userWithName]];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
NSLog(#"USERS: %#",results);
[self.userResults removeAllObjects];
[self.userResults addObjectsFromArray:results];
[[ArrayManager sharedInstance].searchResults addObjectsFromArray:results];
NSLog(#"Count Number: %#", [ArrayManager sharedInstance].searchResults);
[[NSNotificationCenter defaultCenter] postNotificationName:#"reload_data" object:self];
}];
}
}
CHILD VC
-(void)handle_data {
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Object Entries: %lu", (unsigned long)[[ArrayManager sharedInstance].searchResults count]);
NSMutableArray * array = [[ArrayManager sharedInstance] getGlobalArray];
return [array count];
}
- (void)tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *data = [[ArrayManager sharedInstance]init].searchResults[indexPath.row];
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
cell.textLabel.text = data[#"objectId"];
return cell;
}
Data is returned fine from the server however, there is no data being displayed on the table. The ArrayManager class is a Singleton class.
Create a Data Access Object (DAO) and store all information in an array in that.
http://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm
Have both VC's change and access the DAO instead of each other. It is much simpler that way.
I want to be able to display all of the data from two classes from Parse.com but it is not working. Every time I go to open the tableview the app crashes. Here is my code. I used the guidance from https://www.parse.com/questions/query-with-two-classes to aid me.
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
// This table displays items in the class
self.parseClassName = #"rep";
self.parseClassName = #"rep2";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = NO;
self.objectsPerPage = 100;
}
return self;
}
- (PFQuery *)queryForTable {
PFQuery *1query = [PFQuery queryWithClassName:#"rep"];
PFQuery *2query = [PFQuery queryWithClassName:#"rep2"];
PFQuery *query = [PFQuery orQueryWithSubqueries:#[1query,2query]];
[query whereKey:#"user" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
}];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
// Configure the cell to show todo item with a priority at the bottom
cell.textLabel.text = [object objectForKey:#"name"];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Username: %#",
[object objectForKey:#"username"]];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Remove the row from data model
PFObject *objectToDel = [self.objects objectAtIndex:indexPath.row];
[objectToDel deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
UIAlertView *Alert = [[UIAlertView alloc] initWithTitle:#"Item Was Deleted Successfully. Pull Down to Refresh Tab" message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[Alert show];
}
];
}
#end
> Specwatch[668:192464] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'All sub queries of an
> `or` query should be on the same class.'
> *** First throw call stack:
> (0x182319900 0x181987f80 0x182319848 0x1000bd8b4 0x100025c10 0x100072514 0x100071e00 0x18700c0c0 0x1870cbda8 0x1870cbc80
> 0x1870caec8 0x1870caa6c 0x1870ca694 0x1870ca5fc 0x187007778
> 0x184a16b2c 0x184a11738 0x184a115f8 0x184a10c94 0x184a109dc
> 0x184a0a0cc 0x1822d0588 0x1822ce32c 0x1822ce75c 0x1821fd680
> 0x18370c088 0x187074d90 0x10006d054 0x181d9e8b8)
> libc++abi.dylib: terminating with uncaught exception of type NSException
> (lldb)
I'll try to summarize how to go about emancipating yourself from the PFQueryTableViewController (and UITableViewController for that matter. The world would be a slightly happier place if neither of these classes had been invented (imo)). Create a UIViewController subclass called ViewController.
In IB, add a UIViewController (setting its class to ViewController), give it a UITableView constrained to the edges of the view. Hook up the datasource, delegate and an outlet called tableView. Add a prototype cell. For now, just use a standard subtitle or left detail UITableViewCell. Give the cell an identifier of #"cell".
// viewcontroller.m
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property(weak,nonatomic) IBOutlet UITableView *tableView;
#property(strong,nonatomic) NSMutableArray *objects;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[PFCloud callFunctionInBackground:#"getTwoClasses" withParameters:nil block:^(NSArray *objects, NSError *error) {
self.objects = objects;
[self.tableView reloadData];
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
PFObject *object = self.objects[indexPath.row];
cell.textLabel.text = [object objectId];
cell.detailTextLabel.text = [object parseClassName];
return cell;
}
That very simple implementation should be all that's needed on the client. On the server, we just need a cloud function called "getTwoClasses" to get objects from two classes (rep and rep2).
Deploy this function to do that...
Parse.Cloud.define("getTwoClasses", function(request, response) {
var reps;
var user = request.user;
var repQuery = new Parse.Query("rep");
repQuery.equalTo("user", user);
query.find().then(function(result) {
reps = result;
var rep2Query = new Parse.Query("rep2");
rep2Query.equalTo("user", user);
return rep2Query.find();
}).then(function(result) {
response.success(reps.concat(result));
}, function(error) {
response.error(error);
});
});
I am trying to fill a UITableView with the objects of an NSMutableArray which is filled from a table in Parse. The array is definitely being filled (I checked its contents with an NSLog), but the table is staying empty. I have tried A LOT of different ways including the following:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
PFObject *postsObject = [postsArray objectAtIndex:indexPath.row];
cell.textLabel.text = [postsObject objectForKey:#"message"];
cell.textLabel.textAlignment = NSTextAlignmentCenter;
if (!cell) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
if (tableView == table) {
cell.textLabel.text = [postsArray objectAtIndex:indexPath.row];
}
return cell;
}
and the much simpler
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [table dequeueReusableCellWithIdentifier:#"Cell"];
cell.messageLabel.text = [NSString stringWithFormat:[postsArray objectAtIndex:[indexPath row]]];
return cell;
}
Does anyone have any ideas?
Thanks in advance :)
EDIT:
My dataSource methods are:
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return postsArray.count;
}
EDIT #2:
My code I'm using to fill my array and reload my tableView
PFQuery *findData = [PFQuery queryWithClassName:#"AllPosts"];
[findData setLimit:1000];
[findData findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error == nil) {
for (PFObject *object in objects) {
PFObject *post = object[#"content"];
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:post, nil];
postsArray = array;
[table reloadData];
}
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Unable To Load" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
}];
You are doing it wrong , you are replacing the array with new data whenever you are finding a PFObject in objects, your code should be like this:-
//first alloc your array
postsArray=[[NSMutableArray alloc] init];
//Logic goes here
PFQuery *findData = [PFQuery queryWithClassName:#"AllPosts"];
[findData setLimit:1000];
[findData findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error == nil) {
for (PFObject *object in objects) {
PFObject *post = object[#"content"];
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:post, nil];
[postsArray addObject:array];
[table reloadData];
}
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Unable To Load" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
}];
You also need to check if the cell object is actually returned. Also put in the following code after the call to dequeueReusableCellWithIdentifier:
if(!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
This would solve your problem
I have an object called masterMessages filled with objects called messages. Each message object has five keys:
objectId
senderId
senderName
messageBody
timestamp
Basically what I am doing now is querying all the messages sent to my user in this object called masterMessages. Then i'm using:
self.senderIds = [masterMessages valueForKeyPath:#"#distinctUnionOfObjects.senderId"];
to get all the different sender ids (senderId) in an array called senderIds. With this I will populate my conversations table. But i want to populate it with the sender names (senderName) and not the senderIds. I only do it this way in case two users have the same name.
I am trying to find:
How do I say "get valueForKey:#"senderName" for this senderId
and
is there a better way to populate my conversations table?
Here is my code:
note: im using parse.com
-(void)viewWillAppear:(BOOL)animated{
NSString *userId = [[PFUser currentUser] objectId];
PFQuery *query = [PFQuery queryWithClassName:#"lean"];
[query whereKey:#"recipientId" equalTo:userId];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
else {
// We found messages!
masterMessages = objects;
NSLog(#"self.messages = %#", masterMessages);
self.senderIds = [masterMessages valueForKeyPath:#"#distinctUnionOfObjects.senderId"];
NSLog(#"self.senderIds = %#", self.senderIds);
[self.tableView reloadData];
}
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [self.senderIds count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
NSLog(#"self.senderIds = %#", self.senderIds);
NSString *senderDisplayName = [self.senderIds objectAtIndex:indexPath.row];
NSLog(#"sender = %#", senderDisplayName);
cell.textLabel.text = senderDisplayName;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedId = [self.senderIds objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"ShowMissionMessage" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ShowMissionMessage"]) {
[segue.destinationViewController setHidesBottomBarWhenPushed:YES];
MissionChat *missionchatviewcontroller = (MissionChat *)segue.destinationViewController;
missionchatviewcontroller.selectedId = selectedId;
missionchatviewcontroller.masterMessages = masterMessages;
}
}
There are a few issues in the question, one is how to dereference PFObjects, which we took care of on another thread. The rest of this question is about (a) how to use parse objects to build a datasource to support a tableview, and a harder one (b) how to get information from related objects.
Starting with (b), the harder one: There are a few ways to relate objects. Your choice a string-typed column containing the related object id, is intuitive (especially if you have an SQL background), but the least advisable. The better (best) way to model a one-to-one or small one-to-many relation is with a pointer (or array of pointers if one-to-many).
So I think your senderId and recipientId string columns should be replaced by pointer-typed columns called sender and recipient. The huge advantage of this is the ability to eagerly fetch those pointed-to objects on the message (or "lean" in your terms) query.
Having made that change, you're new improved query looks like this:
PFQuery *query = [PFQuery queryWithClassName:#"lean"];
// notice the first change for the better here:
[query whereKey:#"recipient" equalTo:[PFUser currentUser]];
[query orderByDescending:#"createdAt"];
// notice the really valuable feature here:
[query includeKey:#"sender"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// using the array of PFObjects understanding from your other question...
for (PFObject *pfObject in objects) {
NSString *messageBody = [pfObject objectForKey:#"messageBody"];
// these lines here are the punch line:
PFUser *sender = [pfObject objectForKey:#"sender"];
NSString *senderName = [sender username];
NSLog(#"The message %# was sent by %#", messageBody, senderName);
}
}];
The important thing to notice above is that we were able to ask resulting objects for the #"sender" column, and, because you've changed it to a pointer, and because you've done an includeKey on the query, that complete object (e.g. including the PFUser username) is now fetched.
Now the easy question (a). Now that you have the data right from the server, the datasource for the table is nothing more than the returned objects. In other words, throw away the the senderIds array and replace it with:
#property(nonatomic, strong) NSArray *messages;
Your find block becomes trivial:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
self.messages = objects;
}];
Answer messages.count for numberOfRowsInSection, and then pick what you need from the objects in cellForRowAtIndexPath...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
PFObject *message = self.messages[indexPath.row];
NSString *messageBody = [message objectForKey:#"messageBody"];
PFUser *sender = [message objectForKey:#"sender"];
NSString *senderName = [sender username];
cell.textLabel.text = senderName;
return cell;
}
function findObjectByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
}
var obj = findObjectByKey(objArray, 'id', 3);
//ES6
var obj = objArray.find(function (obj) { return obj.id === 3; });
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (i == key && obj[key] == val) {
objects.push(obj);
}
}
return objects;
}
var result = getObjects(obj, 'category_id', valu.category_id);
console.log(result);
I am purposely creating a empty Array to not display anything on the UITablewView.
However, it gives me that error.
To debug, I even created an empty UITableViewController and refer storyboard file to this. However, it is giving me the same error.
I just tried and connect it with an empty UIViewController, it is giving me the same objectAtIndex error.
So I doubt it is the problem with the what I am indexing for cells.
When I run, the screen is shown but it throws the error and it freezes.
The declaration of the newsList is:
#property (strong, nonatomic)NSArray *newsList
This is what I have for the UITableViewController.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.currentUser = appDelegate.currentUser;
NSString *addNewsFeed = #"_NewsFeed";
if (self.currentUser)
{
if (appDelegate.selectedGroup == nil)
{
self.newsList = nil;
}
else
{
NSLog(#"SELECTED GROUP EXIST");
NSString *currentNewsFeed = [appDelegate.selectedGroup[#"name"] stringByAppendingString:addNewsFeed];
PFQuery *query = [PFQuery queryWithClassName:currentNewsFeed];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error)
{
NSLog(#"Error: %#, %#", error, [error userInfo]);
}
else
{
self.newsList = objects;
[self.tableView reloadData];
}
}];
}
}
else
{
NSLog(#"%#", appDelegate.currentUser);
[self performSegueWithIdentifier:#"loginView" sender:self];
}
NSLog(#"ZXCVZCVZ: %#", self.newsList);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (appDelegate.selectedGroup == nil)
{
NSLog(#"NO CELL HERE");
return 0;
}
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (appDelegate.selectedGroup == nil)
{
NSLog(#"NO CELL");
return 0;
}
return [self.newsList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"NO LIST FOUND");
static NSString *CellIdentifier = #"News";
NSLog(#"DSFSDFSDFSFS");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFObject *item = [self.newsList objectAtIndex:indexPath.row];
cell.textLabel.text = item[#"title"];
cell.detailTextLabel.text = item[#"news"];
return cell;
}
you need to allocate the memory for array as below
self.newsList=[[NSMutableArray alloc]init];//At viewWIllAppear
Without alloc the self.newsList you cannot able to store any records in it...
Hope it fixes...