I was normally trying to load the image for each cell like so in my UITableView using Parse SDK:
PFRelation *relation = [object relationForKey:#"images"];
PFQuery *relationQuery = [relation query];
[relationQuery getFirstObjectInBackgroundWithBlock:^(PFObject *obj, NSError *error) {
if (!error)
{
// Adding Image to ImageView
if ([obj objectForKey:#"image"])
{
PFFile *image = (PFFile *)[obj objectForKey:#"image"];
[image getDataInBackgroundWithBlock:^(NSData *data, NSError *error){
if (error)
{
cell.carPhoto.image = [UIImage imageNamed:#"placeholder.png"];
}
else
{
UIImage *carImage = [UIImage imageWithData:data];
cell.carPhoto.image = carImage;
}
}];
}
else
{
cell.carPhoto.image = [UIImage imageNamed:#"placeholder.png"];
}
}
}];
The issues with that, is that it is using way too many queries and the cells have a little lag when scrolling and show the pic above for a brief second before the actual. I attempted to change my code to this to use SDWebImage:
PFRelation *relation = [object relationForKey:#"images"];
PFQuery *relationQuery = [relation query];
[relationQuery getFirstObjectInBackgroundWithBlock:^(PFObject *obj, NSError *error) {
if (!error)
{
// Adding Image to ImageView
if ([obj objectForKey:#"image"])
{
PFFile *image = (PFFile *)[obj objectForKey:#"image"];
NSString *theUrl = image.url;
NSLog(#"%#", theUrl);
[cell.carPhoto setImageWithURL:[NSURL URLWithString:[theUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]
placeholderImage:nil];
}
else
{
cell.carPhoto.image = [UIImage imageNamed:#"placeholder.png"];
}
}
}];
But now I keep getting: -[UIImageView setImageWithURL:placeholderImage:]: unrecognized selector sent to instance 0x7c04ab40. Basically my end goal is to have it so images aren't queried every time the tableView is scrolled, and are cached. Any ideas?
Related
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 am using Parse as a backend of my app, and it seems that the profile photo is not displaying properly, shown in the image:
there is a black strip on john_appleseed's photo.
here is my code for saving the profile image:
NSData *profileData = UIImagePNGRepresentation(cell1.profileView.image);
PFFile *profileFile = [PFFile fileWithName:#"profilePhoto" data:profileData];
[profileFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (!error)
{
if (succeeded)
{
[user setObject:profileFile forKey:#"profilePhoto"];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (!error)
{
}
else
{
}
}];
}
}
}];
here is how I retrieve the image:(inside PFQueryTableViewController)
- (PFQuery *)queryForTable
{
//NSLog(#"called");
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSString *filter = [defaults objectForKey:#"topicFilter"];
NSLog(#"queryfortable: %#", filter);
PFQuery *query = [PFQuery queryWithClassName:#"Questions"];
[query includeKey:#"user"];
[query whereKey:#"category" equalTo:filter];
[query orderByDescending:#"createdAt"];
return query;
}
- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == self.objects.count)
{
return nil;//this is for the load more cell.
}
return [self.objects objectAtIndex:indexPath.section];
}
in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
PFUser *user = [object objectForKey:#"user"];
PFImageView *profileImgView = (PFImageView *)[cell viewWithTag:1];
profileImgView.layer.cornerRadius = profileImgView.frame.size.height/2;
profileImgView.layer.masksToBounds = YES;
PFFile *file = user[#"profilePhoto"];
profileImgView.file = file;
[profileImgView loadInBackground];
any ideas? many thanks.
You should be updating user interfaces on the main thread. Since your loading something in the background, you should notify the main thread that it needs to update an object. loadInBackground is downloading the file asynchronously.
Here is an example, that you can alter for your needs, just to illustrate, that updating UI components in the call back has it's benefits; this is based off of Parses own AnyPic:
NSString *requestURL = file.url; // Save copy of url locally (will not change in block)
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
//dispatch on main thread
UIImage *image = [UIImage imageWithData:data];
} else {
NSLog(#"Error on fetching file");
}
}];
I have a class called "images" on my parse.com app, this class has a column called "imageFile" which contains a .png image, what I'm wondering is how do i set parseImage to be the image taken in from parse. This is what I have so far.
PFQuery *query = [PFQuery queryWithClassName:#"images"];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
NSLog(#"Retrieved data");
if (!error) {
PFFile *file = [object objectForKey:#"imageFile"];
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
parseImage = [UIImage imageWithData:data];
// image can now be set on a UIImageView
}
}];
//parseImage.file = file;
//[imageHolder loadInBackground];
}
}];
parseImage.file = file;
[parseImage loadInBackground];
PFImageView loads asynchronously. Use that instead of manually retrieving the file.
I am trying to get a file from my PFUser object with by simply:
PFFile *userImageFile = gaUser[#"#userImage"];
This returns nil.
This is what it looks like in my method:
- (void)getRemoteImageAndSaveLocalWithObjectId:(NSString *)objectId andType:(NSString *)type{
if ([type isEqualToString:#"user"]) {
PFQuery *query = [GAUser query];
// I tried the include key but I get an error.
//[query includeKey:#"userImage"];
[query getObjectInBackgroundWithId:objectId block:^(PFObject *object, NSError *error) {
GAUser *gaUser = (GAUser *)object;
// this is nil
PFFile *userImageFile = gaUser[#"userImage"];
if (userImageFile){
[userImageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
NSLog(#"in get data");
if (!error) {
NSLog(#"remote image name: %#",data.debugDescription);
UIImage *image = [UIImage imageWithData:data];
}else{
NSLog(#"there was an error");
}
}];
}else{
NSLog(#"user image file is nil");
}
}];
}
This prints: user image file is nil
My parse console looks like:
What am I doing wrong?
Since in Parse your column is named userImage, you should be accessing it with:
PFFile *userImageFile = gaUser[#"userImage"];
instead of
PFFile *userImageFile = gaUser[#"#userImage"];