I'm currently having some trouble with data getting lost when transferring from a ViewController to a subclass of PFFile. The data being passed is image data to upload to a users profile. Here's the code for selecting the image:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// Access the uncropped image from info dictionary
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
// Dismiss controller
[picker dismissViewControllerAnimated:YES completion:nil];
// Resize image
_focusedImage.image = image;
NSData *imageData = UIImageJPEGRepresentation(image, 0.05f);
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:imageData];
[[imageUpload uploadImage] setImagePFFile:imageFile];
}
The Log on imageFile in this view is printing out correctly. However, when I pass the data through to my singleton class imageUpload uploadImage This is what the data structure looks like:
+ (imageUpload *) uploadImage
{
static imageUpload*_sharedImageUpload = nil;
_sharedImageUpload = [[self alloc] init];
_sharedImageUpload.imageData = [[NSData alloc] init];
PFUser *user = [PFUser currentUser];
_sharedImageUpload.imagePFFile = [[PFFile alloc] init];
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:_sharedImageUpload.imageData];
[imageFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
[user setObject:imageFile forKey:#"image"];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
NSLog(#"This should be the profile image upload");
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
}
}];
return _sharedImageUpload;
}
When I get to this point, the system just uploads a blank file (zero bytes) to Parse. The naming is right and its going in the right place on the database, but somewhere along the line the data in the file itself is being lost. I can't figure out why. Does anyone have any suggestions?
It looks like you're confusing objects and methods. What you want is a singleton object that has a method / function that uploads your image. I think this is what you're looking for:
//ImageUploader.h
#import <Foundation/Foundation.h>
#interface ImageUploader : NSObject
+ (instancetype)uploader;
- (void)uploadImageFile:(PFFile *)aFile;
#end
//ImageUploader.m
#import "ImageUploader.h"
#implementation ImageUploader
+ (instancetype)uploader {
static ImageUploader * _uploader = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_uploader = [[self alloc] init];
});
return _uploader;
}
-(void)uploadPFFile:(PFFile *)imageFile{
[imageFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
[user setObject:imageFile forKey:#"image"];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
NSLog(#"This should be the profile image upload");
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
}
}];
}
#end
You invoke it by calling [[ImageUploader uploader]uploadImageFile:someFile].
Related
I am using a MTBBarcodeScanner interface to implement a barcode scanner application.
I need to get the still image of the scanner in my code, so I am trying to call the function:
- (void)captureStillImage:(void (^)(UIImage *image, NSError *error))captureBlock {
if ([self isCapturingStillImage]) {
if (captureBlock) {
NSError *error = [NSError errorWithDomain:kErrorDomain
code:kErrorCodeStillImageCaptureInProgress
userInfo:#{NSLocalizedDescriptionKey : #"Still image capture is already in progress. Check with isCapturingStillImage"}];
captureBlock(nil, error);
}
return;
}
AVCaptureConnection *stillConnection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
if (stillConnection == nil) {
if (captureBlock) {
NSError *error = [NSError errorWithDomain:kErrorDomain
code:kErrorCodeSessionIsClosed
userInfo:#{NSLocalizedDescriptionKey : #"AVCaptureConnection is closed"}];
captureBlock(nil, error);
}
return;
}
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:stillConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (error) {
captureBlock(nil, error);
return;
}
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:jpegData];
if (captureBlock) {
captureBlock(image, nil);
}
}];
}
From my viewcontroller I am calling this function like:
UIImage *img;
NSError *e;
[_scanner captureStillImage:img :e];
but giving me the error:
No visible #interface for 'MTBBarcodeScanner' declares the selector 'captureStillImage::
How can I call this function my UIViewcontroller subclass?
The syntax of your block is incorrect. It should be the following:
[_scanner captureStillImage:^(UIImage *image, NSError *error) {
}];
Also, this is a callback function, you are not supposed to feed parameters into it, these are being returned from it.
If you would like to have variables representing the return values of the callback function outside you callback, you need to declare __block variables.
__block UIImage* img;
__block NSError* e;
[_scanner captureStillImage:^(UIImage *image, NSError *error) {
img = image;
e = error;
}];
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 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?
I'm using Parse with Facebook login to create a new user. I get a few fields from Facebook like their name and location, request their profile image to load in the background before I segue to my main view controller which has a PFImageView for the profile image.
The problem is that the PFFile user[#"profileImage"] is still nil by the time the main view controller's viewDidLoad fires, so there's nothing to loadInBackground.
I don't want to wait for the profile image to load before going to the main view controller...that would take some random time and just be bad.
So, how to I deal with a PFFile that hasn't loaded before I give it to the PFFImageView? Some kind of placeholder?
Here's my code:
------- in LoginViewController.m
- (void) pressedFacebookLogin {
[PFFacebookUtils logInWithPermissions...
if(user.isNew) {
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSDictionary *userData = (NSDictionary *)result;
user[#"fullName"] = userData[#"name"];
// get location, relationship if available
[user saveInBackground];
// start getting the user's profile image before going to main view controller
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:pictureURL];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError == nil && data != nil) {
UIImage *tmpImage = [UIImage imageWithData:data];
NSData *imageData = UIImagePNGRepresentation(tmpImage);
PFFile *imageFile = [PFFile fileWithName:#"image.png" data:imageData];
user[#"profileImage"] = imageFile;
[user saveInBackground];
}
}];
[self segueToMainViewController];
}];
}else {
// deal with existing user...
}
}];
}
-------------- in MainViewController.m
- (void) viewDidLoad {
[super viewDidLoad];
PFUser *user = [PFUser currentUser];
self.profileImageView.image = [UIImage imageNamed:#"placeholder"];
PFFile *profileImageFile = user[#"profileImage"];
// at this point profileImageFile is nil
self.profileImageView.file = profileImageFile;
[self.profileImageView loadInBackground];
}
In the place where you get the profileImageFile as empty add this line.
I guess the profileImageView you use is a ImageView.
#property (weak, nonatomic) IBOutlet UIImageView *profileImageView;
// at this line where you get profileImageFile as nil
[self.profileImageView setImageWithURL:[NSURL URLWithString:user[#"profileImage"]] placeholderImage:[UIImage imageNamed:#"default_profileImage"]];
So I have the first class:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *chosenImage = info[UIImagePickerControllerOriginalImage];
self.imageView.image = chosenImage;
_camera_button.hidden = true;
_camera_imageview.hidden = false;
[_next_camera setEnabled:YES];
NSData *imageData = UIImagePNGRepresentation(chosenImage);
[self upload_selfie:imageData];
[picker dismissViewControllerAnimated:YES completion:NULL];
}
And I am wanting to transfer the variable: imageData to this class:
- (IBAction)upload_selfie:(NSData *)sender{
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:imageData];
// Save PFFile
[imageFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
// Hide old HUD, show completed HUD (see example for code)
// Create a PFObject around a PFFile and associate it with the current user
PFObject *userPhoto = [PFObject objectWithClassName:#"UserPhoto"];
[userPhoto setObject:imageFile forKey:#"imageFile"];
// Set the access control list to current user for security purposes
userPhoto.ACL = [PFACL ACLWithUser:[PFUser currentUser]];
PFUser *user = [PFUser currentUser];
[userPhoto setObject:user forKey:#"user"];
[userPhoto saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
// _loader6.hidden = true;
// [self performSegueWithIdentifier:#"toquestion" sender:self];
}
else{
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
}];
}
Where I have tried:
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:imageData];
But I receive the error "Use of undeclared identifier"?ces where I refrence imageData should I somewhere else?
Also these are the only two pla
Please may you explain why and what I can do about this?
You receive this error because your variable name is sender:
- (IBAction)upload_selfie:(NSData *)sender
You need change your code to:
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:sender];
PS. It seems that you use upload_selfie: method for two aims: a) interface builder action handler; b) method for saving photo. It is better to make two separate methods.
You're passing the parameter as sender so that's the name you should use: PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:sender];.
Though, to be more understandable, choosing a different name would be good.