I'm retrieving data from my parse database and looping it into an NSMutableArray. This is in my getHomes method, which is being called in viewDidLoad. The NSMutableArray contain various UIImages, which CGSize' i'm looping through. This method is called getCellHeights. I want to call this method right after the getHomes method, but stacking them like this in viewdidLoad does not do the trick. The getCellHeights method is not running since there is no objects in the NSMutableArray, cause its not completed with the retrieving of data.
How can i make sure that the getCellHeights is not running before the getHomes method is completed?
getCellHeights
-(void)getcellHeights {
for (int i = 0; i < homesDic.count; i++) {
UIImage *image = [[homesDic objectAtIndex:i] objectForKey:#"image"];
CGFloat divider = image.size.width/145;
CGFloat wantedWidth = 145;
CGFloat wantedHeight = image.size.height/divider;
NSLog(#"width: %f height: %f", wantedWidth, wantedHeight);
CGSize size = CGSizeMake(wantedWidth, wantedHeight);
[_cellSizes addObject:[NSValue valueWithCGSize:size]];
}
}
getHomes
-(void)getHomes {
PFQuery *query = [PFQuery queryWithClassName:#"Homes"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects1, NSError *error) {
if (!error) {
for (PFObject *object in objects1) {
PFQuery *userQ = [PFUser query];
[userQ getObjectInBackgroundWithId:[object objectForKey:#"userId"] block:^(PFObject *userName, NSError *error) {
NSLog(#"%#", userName);
[[userName objectForKey:#"file"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
UIImage *profileImage = [UIImage imageWithData:data];
PFQuery *homeQ = [PFQuery queryWithClassName:#"homeImages"];
[homeQ whereKey:#"homeId" equalTo:object.objectId];
[homeQ whereKey:#"number" equalTo:#(0)];
[homeQ findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
PFQuery *cityQ = [PFQuery queryWithClassName:#"City"];
id zip = [NSNumber numberWithInteger: [[object objectForKey:#"zip"] intValue]];
[cityQ whereKey:#"fra" greaterThanOrEqualTo:zip];
[cityQ whereKey:#"fra" lessThanOrEqualTo:zip];
[cityQ findObjectsInBackgroundWithBlock:^(NSArray *cObject, NSError *error) {
if (!error) {
PFObject *cityObject = [cObject lastObject];
PFObject *image = [objects lastObject];
[[image objectForKey:#"Image"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:data];
NSMutableDictionary *flagDict = [[NSMutableDictionary alloc] init];
[flagDict setObject:[object objectForKey:#"title"] forKey:#"title"];
[flagDict setObject:image forKey:#"image"];
[flagDict setObject:[cityObject objectForKey:#"navn"] forKey:#"city"];
[flagDict setObject:[userName objectForKey:#"name"] forKey:#"name"];
[flagDict setObject:profileImage forKey:#"profileImage"];
[homesDic addObject:flagDict];
[self.collectionView reloadData];
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
}];
}
}];
}];
}];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
The Blocks that you're passing to the Parse framework query methods are completion/callback Blocks; when the fetch has completed, they are run. Do whatever you need to do after the fetch -- including calling cellHeights -- in the appropriate completion Block.
Related
I am running and application that when you like or dislike another user the result is stored in either an array of "accepted" (liked) or array of "rejected" disliked. The array contains the users objectId that you liked/disliked.
When I am querying for results I am using the whereKey:notContainedIn method:
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" notContainedIn:[PFUser currentUser][#"accepted"]];
[query whereKey:#"objectId" notContainedIn:[PFUser currentUser][#"rejected"]];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (!error) {
for (PFObject *object in objects) {
self.displayedUserId = object.objectId;
NSLog(#"userId: %#", self.displayedUserId);
PFFile *imageFile = object[#"image"];
[imageFile getDataInBackgroundWithBlock:^(NSData * _Nullable data, NSError * _Nullable error) {
if (!error) {
UIImage *image = [UIImage imageWithData:data];
self.userImage.image = image;
}else {
NSLog(#"%#", error);
}
}];
}
}else {
NSLog(#"%#", error);
}
}];
An error of:
[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]
I am not to sure how to debug or next steps.
when the code run it doesn't call collectionView reloadData
photoArray is NSMutableArray and initialized in viewDidLoad
photoArray=[[NSMutableArray alloc] init];
and parse query
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
[SVProgressHUD show];
PFFile *thumbnail = [object objectForKey:#"photo"];
[thumbnail getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
NSData *imageData = data;
UIImage *image = [UIImage imageWithData:imageData];
[photosArray addObject:image];
[self.collectionView reloadData];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
[SVProgressHUD dismiss];
}];
}
}
Note the same code was working in another project , so how to solve it
The reason it's not working is probably because you're reloading from a background thread. Wrap the call to reloadData as follows:
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
As an aside, don't do this inside the loop which adds objects to your datasource (photosArray). Move that dispatch below the loop.
I am retrieving data from Parse.com and try to add an image with this data to an array. However, some code is not being executed without any obvious reason. This is the code:
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:#"objectId" containedIn:[self.event objectForKey:#"members"]];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
self.membersArray = [NSMutableArray arrayWithArray:users];
[self.tableView reloadData];
for (PFUser *user in self.membersArray) {
[self.memberPhotosArray addObject:[NSNull null]];
NSInteger index = [self.memberPhotosArray indexOfObject:user];
PFFile *photoFile = [user objectForKey:#"profilePicture"];
[photoFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) {
if (!error) {
NSLog(#"Before creating image");
UIImage *image = [UIImage imageWithData:result];
[self.memberPhotosArray[index] addObject:image];
NSLog(#"After creating image");
[self.tableView reloadData];
}
}];
}
}
}];
The first log is being executed, everything after this line is not being called. Can someone explain why and provide a solution?
Looks like your code is running on a background thread. Run the code in the final callback block on the main thread.
if (!error) {
NSLog(#"Before creating image");
UIImage *image = [UIImage imageWithData:result];
dispatch_async(dispatch_get_main_queue(), ^{
[self.memberPhotosArray[index] addObject:image];
NSLog(#"After creating image");
[self.tableView reloadData];
});
}
UI related operations should only be performed on the main thread.
I am using Parse.com
When I download my PFFile(contains image) in viewDidAppear my app freezes for a while and then it shows the pic. But that is not right. I want UIActivityIndicatorView to be animated or at least shown. I know that this deals with async or something. But I have no idea how to make that work correctly.
PFFile* imageFile = [[PFUser currentUser] objectForKey:#"profilePic"];
if (imageFile) {
self.activity.hidden = NO;
[self.activity startAnimating];
NSURL* imageFileUrl = [[NSURL alloc]initWithString:imageFile.url];
NSData* imageData = [NSData dataWithContentsOfURL:imageFileUrl];
self.profilePic.image = [UIImage imageWithData:imageData];
}
This downloads the image and shows it.
UPD:
PFQuery* query = [PFUser query];
[query valueForKey:#"profilePic"];
[query findObjectsInBackgroundWithBlock:^(NSArray* data, NSError* error)
{
PFFile* imageFile = data[0];
[imageFile getDataInBackgroundWithBlock:^(NSData* data,NSError* error){
if (!error) {
self.activity.hidden = NO;
[self.activity startAnimating];
NSURL* imageFileUrl = [[NSURL alloc]initWithString:imageFile.url];
NSData* imageData = [NSData dataWithContentsOfURL:imageFileUrl];
self.profilePic.image = [UIImage imageWithData:imageData];
}else{
self.profilePic.image = [UIImage imageNamed:#"profile#2.png"];
}
}];
}];
UPD 2:
This solved my problem. Both answers were helpful.
PFQuery* query = [PFUser query];
[query getObjectInBackgroundWithId:[PFUser currentUser].objectId block:^(PFObject* object, NSError* error){
if(!error){
PFFile* imageFile = object[#"profilePic"];
[imageFile getDataInBackgroundWithBlock:^(NSData* data, NSError* error) {
if (!error) {
self.activity.hidden = NO;
[self.activity startAnimating];
self.profilePic.image = [UIImage imageWithData:data];
NSLog(#"Profile pic shown");
}
else{
NSLog(#"Error 2: %#",error);
}
}];
}else{
self.profilePic.image = [UIImage imageNamed:#"profile#2.png"];
NSLog(#"Fail 1 : %#",error);
}
}];
This is because you are not getting the object in the background. Try using a query to accomplish this. If you need more info I can give you the full query as I have this code inside my app :)
Sample code:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {}];
To get an image with data:
PFFile *imageFile = [object objectForKey:#"YOURKEY"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {}];
There are couple of things wrong with your code
Your query is not right, I can't understand this part [query valueForKey:#"profilePic"]; if you want to get user have profile pics then you should do something like this [query whereKeyExist:#"profilePic"];, but at the moment query you have will bring down all objects.
You are downloading image in main thread which make your app free you should do something like this
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error)
{
self.profilePic.image = [UIImage imageWithData:imageData];
}
}];
You must check data.count before calling data[0], this could crash your application if data array is nil or empty.
Update:
Getting profile pic for currentUser will be something like this
PFUser *currentUser = [PFuser currentUser];
if (!currentUser)
{
//need to login
}
else
{
// may be you need to fetch
__weak typeof(self) weakSelf = self;
[currentUser fetchIfNeededInBackgroundWithBlock:^(PFObject *obj, NSError *error){
PFFile* imageFile = [[PFUser currentUser] objectForKey:#"profilePic"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error)
{
weakSelf.profilePic.image = [UIImage imageWithData:imageData];
}
}];
}];
}
I set up a profile picture for users following the code here, but I can't seem to retrieve the picture. The code I tried is below please help me out can't seem to figure this out:
PFQuery *query = [PFUser query];
[query getObjectInBackgroundWithId:#"profilePic" block:^(PFObject *object, NSError *error) {
if (!error) {
NSData * imageData = UIImagePNGRepresentation(object);
}
else{
// Error stuff
}
}];
You've misunderstood the purpose of getObjectInBackgroundWithId:block: ... the first parameter is meant to be the ID of the object you want to get. Inside the block you can then query for properties of the object you've gotten.
e.g. if you want the current user:
PFQuery *query = [PFUser query];
[query getObjectInBackgroundWithId:[PFUser currentUser].objectId block:^(PFObject *user, NSError *error) {
if (!error) {
PFFile *profilePic = user[#"profilePic"];
[profilePic getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:imageData];
}
}];
}
else{
// Error stuff
}
}];