Post a UIImage to the web
So here is something a lot of people have been wondering to do in the forums. How do I take a UIImage or any image and post it to a website. So this will go over how to do that.
There are two ways to tackle this issue. One we can base64 encode the file and post is normally inside a XML or JSON instance or we can simulate a normal HTML post. This tutorial will go over the HTML post.
This will start off where Using a UIImagePickerController left off. So you can grab the code and start there if you want. So lets begin.
So first open up testPickerViewController.h and we want to add in one outlet and one action.
So here is the outlet we want to add
IBOutlet UIButton *upload;
Here is the action we want to add
IBOutlet UIButton *upload;
Now double click on testPickerViewController.xib and we need to connect the new outlet and action. We also need to create our new upload button. So drag around the current items and place the new button under the grab button. Then you want to make the button hidden by default. The option is as shown below. Do get to that selector you do Tools -> Attributes Inspector
You might also want to setup your UIImageView to aspect fit. If the image is larger then your box you created in IB it will shrink the image to fill it. Click on the UIImageView and in the Attributes Inspector select the following drop down for Mode.
Now you want to make your connections to the new outlet and action we created in code. So here is another screenshot of what they should look like
Now it is back to the code. So save this and you can quit IB.
So open up testPickerViewController.m and find the imagePickerController method and at the end add
upload.hidden = NO;
That will show our upload button once a image is set.
So now we need to create our uploadImage method that gets called then the button is pressed. So it is below and hopefully pretty well commented.
- (IBAction)uploadImage {
/*
turning the image into a NSData object
getting the image back out of the UIImageView
setting the quality to 90
*/
NSData *imageData = UIImageJPEGRepresentation(image.image, 90);
// setting up the URL to post to
NSString *urlString = @"http://iphone.zcentric.com/test-upload.php";
// setting up the request object now
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];
/*
add some header info now
we always need a boundary when we post a file
also we need to set the content type
You might want to generate a random boundary.. this is just the same
as my output from wireshark on a valid html post
*/
NSString *boundary = [NSString stringWithString:@"---------------------------14737809831466499882746641449"];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField: @"Content-Type"];
/*
now lets create the body of the post
*/
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"ipodfile.jpg\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imageData]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[request setHTTPBody:body];
// now lets make the connection to the web
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSLog(returnString);
}
So now if you build and go you will upload the image you selected to the following image URL
http://iphone.zcentric.com/uploads/ipodfile.jpg
Below is my PHP file I am using to handle uploads.
$uploaddir = './uploads/';
$file = basename($_FILES['userfile']['name']);
$uploadfile = $uploaddir . $file;
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "http://iphone.zcentric.com/uploads/{$file}";
}
It is nothing special. I do an echo so you can see in the console what the file is. If you switch the filename=”" part in the uploadImage section it will be placed as another file.
Files will be removed once they are 10 minutes old on the server so they won’t last but you can use it as a playground.
As always here is my code.
Using a UIImagePickerController
Apple allows you great access into the images you have taken with your camera or saved on the phone via Safari or such. It also allows you to load up the camera very easily in your code to take pictures from your application.
So the first thing we want to do is start a new project and call it testPicker. You want to create a View based application.
Now that our application is created we want to open up IB and create our view. So double click on the testPickerViewController.xib file and it will launch IB. The first thing you will notice is that is is a blank view. So lets grab a round rectangle button on the view and also a image view controller. So now our view should look something like this
So now we have to create 2 outlets and an action. The outlets are for the items we placed on the view and the action is for the button click. So make sure you are selecting the File’s Owner name and it should be of type testPickerViewController and create 2 outlets and an action. Name them like the following image.
Now we need to connect them. So you just drag the outlet to the correct item on the view and then grab the action over to the button and a box will popup and select touch up inside as an option. It should look like the following
Now we have to write the new class file. Make sure you are still selected to File’s Owner and go to File -> Write Class Files. You should get the following if you did it the correct spot.
Save the file. It will ask you to merge or overwrite. For this just overwrite the files.
Now that we have our view setup, lets do some code. So the first thing is we must set the UIImagePickerController delegates.
So open up testPickerViewController.h and we want to add in the following to the class reference.
Now we need to add the reference to our image picker so add the following
UIImagePickerController *imgPicker;
Now we need a property for it.
@property (nonatomic, retain) UIImagePickerController *imgPicker;
So the whole file looks something like this now
@interface testPickerViewController : UIViewController{ IBOutlet UIButton *button; IBOutlet UIImageView *image; UIImagePickerController *imgPicker; } - (IBAction)grabImage; @property (nonatomic, retain) UIImagePickerController *imgPicker; @end
Now save the file and open up testPickerViewController.m. First we need to synthesize the image picker
@synthesize imgPicker;
Now we need to create the viewDidLoad method and init the image picker.
- (void)viewDidLoad {
self.imgPicker = [[UIImagePickerController alloc] init];
self.imgPicker.allowsImageEditing = YES;
self.imgPicker.delegate = self;
self.imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
There are 3 different types of sources you can use
- UIImagePickerControllerSourceTypeCamera : to load the camera
- UIImagePickerControllerSourceTypePhotoLibrary : to load images from library
- UIImagePickerControllerSourceTypeSavedPhotosAlbum : loads all images saved
Also we set the editing to YES. This will allow your user to crop the image and such. The picker controller’s delegate function we will add later will return the information so you can edit the UIImage that is returned if you really wanted. We will not cover that here. At least not yet.
Now lets load the picker on the button click so make the grabImage method look like this
- (IBAction)grabImage {
[self presentModalViewController:self.imgPicker animated:YES];
}
Now if we click build and go, you will be able to click on the grab images button and your image picker will slide up from the bottom and allow you select a image.
Now we need to add in our delegate methods so we can handle the image when a user selects it. The only one we will really cover is imagePickerController. Here is my complete function to take the selected image and then place it into the imageview we created in IB.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
image.image = img;
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
}
Here is our final product.
As always you can grab the code here.
Using threads for a JSON request
Make sure you have JSON Framework installed first.
This is a continuation of Post JSON to a webservice
So if you are writting your webservice application and you have to wait a few seconds to make the request and get the response you will notice that your main application gets locked up. This is due to having to wait for the request to complete and nothing else can work till that request is completed.
So here is where we introduce threads. So the idea is this. We will click on a cell and load the view. It will be a blank view and then we will launch a thread to grab the information. Once we have that information the thread will complete and we can load the table.
So we will start off using the following source code. This is code from the UIImageView in a custom cell example.So for here we need to change our PHP JSON endpoint. We want to simulate the wait so we will just toss in a sleep(5); or such. So the endpoint now looks like this
header('Content-type: application/x-json');
$id = (int)$_GET['id'];
$array = array(
1 => array(
'id' => 1,
'title' => 'Brown Dog',
'img' => 'http://iphone.zcentric.com/files/1.jpg',
),
2 => array(
'id' => 2,
'title' => 'Black Dog',
'img' => 'http://iphone.zcentric.com/files/2.jpg',
),
3 => array(
'id' => 3,
'title' => 'Two Dogs',
'img' => 'http://iphone.zcentric.com/files/3.jpg',
),
4 => array(
'id' => 4,
'title' => 'Girl',
'img' => 'http://iphone.zcentric.com/files/4.jpg',
),
);
sleep(5);
echo json_encode($array[$id]);
So lets change the endpoint in the code now. Open up JSONViewController.m and in the viewDidLoad method change the NSURL line to look like.
NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get2.php?id=%@", self.itemID, nil]];
You can see the file use to be called test-json-get.php, I just made it test-json-get2.php. The new file has our sleep in it. If you click build and go now, you will notice that if you click on a cell to view it you will experience some delay and the UI is in a locked state till the 5 seconds are up. Once the 5 seconds are up the new view is displayed.
So our goal is to first load that new view and then display like a activity indicator in the upper right. Now you might be saying.. why use threads? Just display the indicator before making the JSON call. Well the reason is while we make the request is that the UI is locked and the indicator will stop so we want to do the request in a background thread so our main UI will continue to work.
So the first thing we want to do is move the JSON code from viewDidLoad into a new method. Lets call it getJSON. So here is what that function should look like
- (void)getJSON {
NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get2.php?id=%@", self.itemID, nil]];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Down" message:@"The webservice you are accessing is down. Please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
else {
self.jsonItem = [jsonData JSONValue];
// setting up the title
self.jsonLabel.text = [self.jsonItem objectForKey:@"title"];
// setting up the image now
self.jsonImage.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [self.jsonItem objectForKey:@"img"]]]];
}
}
Your viewDidLoad should now be an empty method. So now lets put the activity indicator in there. So now the function should look like
- (void)viewDidLoad {
CGRect frame = CGRectMake(0.0, 0.0, 25.0, 25.0);
UIActivityIndicatorView *loading = [[UIActivityIndicatorView alloc] initWithFrame:frame];
[loading startAnimating];
[loading sizeToFit];
loading.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin);
// initing the bar button
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:loading];
[loading release];
loadingView.target = self;
self.navigationItem.rightBarButtonItem = loadingView;
}
So you can see we are initing a new UIActivityIndicatorView and then creating a new bar button item and using our new indicator view as the bar item’s view. Then we are placing that item in the right hand side of the navigation bar.
So if we click build and go we will get something like this when we click on a cell.
So now we need to do the threading. So our JSON request got moved into a new function so that is where we will do the bulk of the request. So lets set that up.
At the top of the getJSON method you want to put the following code.
NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];
At the bottom of that method you want to put the following
[pool release];
So we are creating an auto release pool. Any items that we are set to autorelease in-between the two lines above won’t be released until we call [pool release]
So now we just need to init the thread. So at the bottom of viewDidLoad method add the following
[NSThread detachNewThreadSelector: @selector(getJSON) toTarget:self withObject:nil];
So that will call the method getJSON in a new thread. Now when you click build and go and click a cell and wait 5 seconds you will see the following.
You will notice that our indicator is still there, so we should remove that. So at the bottom of getJSON we want to remove it once the request is done. So add the following line
self.navigationItem.rightBarButtonItem = nil;
That is all you need for threads. Very simple huh? As always you can grab the code here.
Post JSON to a webservice
Make sure you have JSON Framework installed first.
This is a continuation of UIImageView in a custom cell
Here is a simple little code snippet on how to post some JSON to a webservice.
NSString *requestString = [NSString stringWithFormat:@"json=%@", [loginDict JSONFragment], nil]; NSData *requestData = [NSData dataWithBytes: [requestString UTF8String] length: [requestString length]]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: @"http://domain.com/api/login/"]]; [request setHTTPMethod: @"POST"]; [request setHTTPBody: requestData]; NSData *returnData = [ NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil ]; NSString *returnString = [[NSString alloc] initWithData:returnData encoding: NSUTF8StringEncoding]; // decoding the json NSDictionary *loginArray = [NSDictionary alloc]; loginArray = [returnString JSONValue];
So below is what each line does.
- First we format the request string. What I am doing there is taking a whole dictionary and making it a JSON object. I then assign that to the json variable to post in. So in PHP land that would come in as $_POST['json'] and I could do like json_decode($_POST['json']) and turn it into an array I can work with in PHP
- The next line turns the request into a data object so we know how big the string is.
- Now we setup the URL to post the request to
- We are setting the method to a post in html land it is like <form method=”POST”>
- Now are are setting up the body of the post with our data
- We now make the connection to the server and then get a data string back
- We decode the data stream into a NSString object
- Since you send in JSON I expect you get JSON back so we take the contents of the NSString and turn it into a NSDictionary
Pretty simple. That is all there is to it.
UIBarButtonItem with a custom view
The main project I am doing for Photoblog is an application to show all the latest entries and such. So I call a web service using JSON and it gives me the data I need. Now there might be a few second delay in sending the request and getting the request back. Once I get the request back I open my new view. So one way to show the user that activity is going on is creating a activity indicator.
Apple has UIActivityIndicatorView which we will use. Now I want to put this in the top navigation bar but by default you cannot. So we have to set a UIBarButtonItem then use a custom view option. So here is the function I created to do this
-(void)showLoading {
// initing the loading view
CGRect frame = CGRectMake(0.0, 0.0, 25.0, 25.0);
UIActivityIndicatorView *loading = [[UIActivityIndicatorView alloc] initWithFrame:frame];
[loading startAnimating];
[loading sizeToFit];
loading.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin);
// initing the bar button
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:loading];
[loading release];
loadingView.target = self;
self.navigationItem.rightBarButtonItem = loadingView;
}
This isn’t anything major. It will create the UIActivityIndicatorView with a width and height of 25. It will start is animating and make it fit into the frame we created.
Then I init a UIBarButtonItem and use a initWithCustomView method to set it to use our activity indicator view. I then release the indicator and set the click target to self. I then place that button item on the right side of the bar.
Now I do this right before I begin the process of sending a request and getting the response. The problem is that I never see the animation due to the request and responsive locking the main thread.
So when I have threads figured out we will do the JSON request in a thread so the main thread isn’t locked.












