I load a "Rooms" UICollectionView with specific images that the logged in user has selected in a previous view controller, by populating the "imageFilesArray" and telling the UICollectionViewCells to use its data:
-(void) retrieveSelectedImagesForRooms
{
//parse query where we search the selectedImage array column and return any entry where the array contains the logged in user objectid
PFQuery *getRooms = [PFQuery queryWithClassName:#"collectionViewData"];
[getRooms whereKey:#"selectedImage" equalTo:[PFUser currentUser].objectId];
[getRooms findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
imageFilesArray = [[NSArray alloc] initWithArray:objects];
[roomsCollection reloadData];
}
}];
}
The next page has to show the specific lights that user has selected for that previously selected room image. So I add the row's objectid I've just selected to a new column on Parse, called "clickedRoom", when the room is selected:
-(void)selectedRoom:(PFObject*)object
{
[object addUniqueObject:object.objectId forKey:#"clickedRoom"]; //put object id into clickedRoom column on Parse to save what room you specifically chose so that the light images correspond to only that room
[object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (!error){}
else{}
}];
}
- (void)collectionView:(UICollectionView*)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self selectedRoom:[imageFilesArray objectAtIndex:indexPath.row]];
[self performSegueWithIdentifier:#"myLights" sender:self];
}
Now, in the "Lights" page I need to show ONLY the light images that have the selected room's objectid in that "clickedRoom" column. I believe it's the same principle as how I retrieve the room images, but I can't figure out what I should be querying for, something like:
-(void) retrieveCorrespondingImagesForLights
{
PFQuery *getLights = [PFQuery queryWithClassName:#"collectionViewData"];
[getLights whereKey:#"clickedRoom" equalTo:**MY-PREVIOUSLY-SELECTED-ROW**.objectid];
[getLights findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
imageFilesArray = [[NSArray alloc] initWithArray:objects];
[myLightsCollection reloadData];
}
}];
}
Any suggestions please?!
The retrieveCorrespondingImagesForLights is in a different view controller than your roomsCollection, correct? If so, then you will need to pass the object id of the selected room to the new view controller that is segued to at [self performSegueWithIdentifier:#"myLights" sender:self];
Take a look here Pass Index Number between UITableView List segue
In your case, you should add a property to your destination view controller (I'll call it LightsViewController) to capture the object, or objectId if that's all you need for the query. I would suggest something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"myLights"]) {
// note that "sender" will be the cell that was selected
UICollectionViewCell *cell = (UICollectionViewCell*)sender;
NSIndexPath *indexPath = [roomsCollection indexPathForCell:cell];
LightsViewController *vc = (LightsViewController*)[segue destinationViewController];
vc.selectedObject = indexPath.row;
}
}
Then, in retrieveCorrespondingImagesForLights:
PFQuery *getLights = [PFQuery queryWithClassName:#"collectionViewData"];
[getLights whereKey:#"clickedRoom" equalTo:self.selectedObject.objectid];
EDIT*
Without understanding your exact implementation details, it seems like you are trying to use Parse to pass data between your view controllers when you'd be better suited to do it natively in your app. Maybe I'm misunderstanding your issue.
Related
I have two ViewControllers, in the first one, there are 3 key-value pairs in each PFObject, save them to parse after clicking a button. In the second ViewController, I want to create another property and save it to the same PFObject. Here is my code:
in the first ViewController:
- (void)next
{
PFObject *thisuser = [PFObject objectWithClassName:#"User"];
thisuser[#"name"] = [PFUser currentUser].username;
thisuser[#"institution"] = institution.text;
thisuser[#"major"] = major.text;
[thisuser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (succeeded)
{
GuideViewController2 *GVC2 = [[GuideViewController2 alloc]initWithNibName:#"GuideViewController2" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:GVC2];
nav.modalPresentationStyle = UIModalPresentationFullScreen;
nav.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:nav animated:YES completion:nil];
NSLog(#"success");
}
else
{
NSLog(#"nope");
}
}];
}
and in my second view controller, the user can upload a profile photo, i want this photo to be saved in the same PFObject. So is there a way to retrieve an object using [PFUser currentUser].username property? How do i get this user object under the User class in order to add a photo key-value pair?
thx.
You can just query for the user:
PFQuery *query = [PFUser query];
[query whereKey:USERNAME_KEY equalTo:username];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error)
{
if (!error) {
//You found the user!
PFUser *queriedUser = (PFUser *)object;
}
}];
Supposing the username is actually unique! USERNAME_KEY would be your username field and username the actual username.
I am using this query to find users, it works but it just shows me the first user. I want it to show me the user with the text of an UITextField.
How can I do that ?
(I have a textfield and there I type in a name and then it should show the parsed users with the name)
PFQuery *query = [PFUser query];
NSArray *users = [query findObjects];
userQuerys.text = users[0][#"username"];
Thanks very much
This code will fetch you all the PFUsers in which username is equal to the name parameter:
- (void)searchUsersNamed:(NSString *)name withCompletion:(void (^)(NSArray *users))completionBlock {
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:name];
[query findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
// we found users with that username
// run the completion block with the users.
// making sure the completion block exists
if (completionBlock) {
completionBlock(users);
}
} else {
// log details of the failure
NSLog(#"Error: %# %#", error, [error description]);
}
}];
}
An example, if you need to update the UI with the result, for example, a table:
- (void)someMethod {
// we will grab a weak reference of self to perform
// work inside the completion block
__weak ThisViewController *weakSelf = self;
//replace ThisViewController with the correct self class
[self searchUsersNamed:#"Phillipp" withCompletion:^(NSArray *users) {
//perform non-UI related logic here.
//set the found users inside the array used by the
//tableView datasource. again, just an example.
weakSelf.users = users;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//pefrorm any UI updates only
//for example, update a table
[weakSelf.tableView reloadData];
}];
}];
}
A small note: the completionBlock here wont run if there is an error, but it will run even if no users were found, so you gotta treat that (if needed. in this example, it was not needed).
Avoid running non-UI related logic on that mainQueue method, you might lock the Main thread, and that`s bad user experience.
I'm making watch app for iOS application. I get data from parent application in watch main InterfaceController and pass it to other InterfaceController for creating table. Here code of configuring table:
- (void)configureTableWithData:(NSArray*)dataObjects {
[self.table setNumberOfRows:[dataObjects count] withRowType:#"rowType"];
for (NSInteger i = 0; i < self.table.numberOfRows; i++) {
RowType* row = [self.table rowControllerAtIndex:i];
NSObject* object = [dataObjects objectAtIndex:i];
[row.titleName setText:[object valueForKey:#"CharCode"]];
[row.bottomValue setText:[object valueForKey:#"Value"]];
}
}
When I select row, I want to transfer data back to first page. It's need for changing some label on first page. I'm doing it with transfer data to parent app and return it back to main InterfaceController
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex {
//Here must be dictionary, where I put row
[WKInterfaceController openParentApplication:data reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"data transfer");
}];
[self dismissController];
}
How can I get data of row? (row.titleName, row.value) May be it's stupid question, I am still just a beginner, but I can get it. I tried to print on console row.titleName and row.bottomValue and of course I've get nothing. (sorry for my english, not my mother tongue) What did I miss?
I have same issue with watch kit in tableview. For now we only set the text in cell label not get back so you have to do like this
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex {
//Here must be dictionary, where I put row
NSDictionary* object = [dataObjects objectAtIndex:rowIndex];
NSLog("used object as you want");
[WKInterfaceController openParentApplication:object reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"data transfer");
}];
[self dismissController];
}
Is it possible to use a PFQueryTableView with a User relation? For example I can easily list user relations (favorited items in this case) in a regular Tableview like so:
PFRelation *relation = [self.currentUser relationForKey:#"favorites"];
[[relation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
// There was an error
} else {
// NSLog(#"%#", objects);
salesArray = nil;
salesArray = [[NSMutableArray alloc]initWithArray:objects];
[self.tableView reloadData];
}
}];
But PFQueryTableView requires you to put in a parseClassName. So would I put in the Users class here then overwrite objectsLoad method or something to obtain a specific logged in users favorites?
Not sure if this was the best solution but I created a Dummy class to avoid PFQueryTableView from automatically downloading data. I basically created an empty Class on Parse named Dummy. Then I just set my objects into self.objects and then [self loadObjects]; to refresh the tableview.
I have two issues here.
1) The data in my UITableView does not load when I first open up its ViewController. The proper data does end up appearing, but but only after I add more data to it on another ViewController, and then come back to the TableViewController. Even when doing that, the UITableView is always one item behind, meaning that when I first load the app I will see nothing in my TableView, then if I add an item, for example called "hat" in a second ViewController, I will come back to the TableViewController and see all the items I'd added previously, but I will have to add another item, for example called "chair" to my table in order to see "hat" in my table.
2) While I am able to have users successfully add items to my Parse database and then view the items they've added in the UITableView (albeit with the roadblock addressed in issue 1 above), I am unable to successfully populate the UIImage in each cell in the TableView with the photo file of each cell's corresponding item. The photos are being successfully saved to Parse, so it is definitely a problem querying for them properly, or keying into the exact place they are stored, or simply configuring my subclassed TableViewCell to display them properly.
ItemsTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationItem setHidesBackButton:YES];
}
-(void)viewWillAppear:(BOOL)animated
{
PFQuery *query = [PFQuery queryWithClassName:#"giveItem"];
[query whereKey:#"giver" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.myGiveItems = [[NSMutableArray alloc]init];
for (PFObject *object in objects) {
PFGiveItem *newGiveItem = [[PFGiveItem alloc]init];
newGiveItem.giveItemName = object[#"giveItemTitle"];
// return photo files for each of the objecs
PFFile *giveItemImageFile = object[#"imageFile"];
[giveItemImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *giveItemImageForCell = [UIImage imageWithData:imageData];
newGiveItem.giveItemImage = giveItemImageForCell;
};
}];
[self.myGiveItems addObject:newGiveItem];
}
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.myGiveItems.count;
}
-(void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section
{
tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
}
- (JFGiveItemCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
JFGiveItemCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil){
cell = [[JFGiveItemCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
PFGiveItem *giveItem = self.myGiveItems[indexPath.row];
cell.giveItemLabel.text = giveItem.giveItemName;
cell.giveItemImageView.image = giveItem.giveItemImage;
return cell;
}
My Parse backend is structured as follows
I will be very receptive to any guidance on how to more properly structure my database to be less redundant and more powerful.
Class 1 "Item" with rows:
"itemTitle," a user-input string from the AddItem ViewController
"owner," the user who adds the item (and photo)
"itemPhoto," a pointer to an object of Class 2, "ItemPhoto"
Class 1 "Item" with rows:
"imageOwner," the user who adds the item (and photo)
"imageName," a string the same as the itemTitle from Class 1
"imageFile," a File uploaded by the user
All of these items are saved successfully, but for your better understanding, here is the code I use to do this.
AddItemViewController
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSString *nameForGiveItem = self.giveItemTitleTextField.text;
NSData *giveItemImageData = UIImagePNGRepresentation(self.giveItemImage);
PFFile *giveItemImageFile = [PFFile fileWithName:nameForGiveItem data:giveItemImageData];
PFObject *giveItemPhoto = [PFObject objectWithClassName:#"giveItemPhoto"];
giveItemPhoto[#"imageOwner"] = [PFUser currentUser];
giveItemPhoto[#"imageName"] = nameForGiveItem;
giveItemPhoto[#"imageFile"] = giveItemImageFile;
[giveItemPhoto saveInBackground];
PFObject *giveItem = [PFObject objectWithClassName:#"giveItem"];
giveItem[#"giveItemTitle"] = self.giveItemTitleTextField.text;
giveItem[#"giver"] = [PFUser currentUser];
[giveItem setObject:giveItemPhoto forKey:#"giveItemPhoto"];
[giveItem saveInBackground];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
return YES;
}
For 1)
In viewWillAppear you call [self.tableView reloadData]; outside of the block. That means you actually call it before the block has finished executing - meaning before the data was loaded.
Move it to after the for block but make sure it is executed on the main thread - otherwise it will not influence the appearance of the ui - meangin the table will not be acualized either.
For 2)
The solution for 1) should fix 2) as well. You load your images asynchronously, which is perfect. But when that is finished you do not reload the table. When you manage to get it reloaded once all the data has been loaded then the cell images will be refreshed as well.
3)
You may want to add any type of view/spinner/progress indicator or just a regular lable that indicates to the user that some data is still loading ...