Posterous theme by Cory Watilo

Filed under: mobile development

Intercepting UIButton Touch Events

I was recently building an iPhone app that required a UIButton be held down and then released after a certain period of time had elapsed. The problem I encountered was that testing for touchesBegan didn't work for buttons. I'm not talking about UIControlEvents. I didn't need to know that button had been tapped or it's current state. I needed to know when the button was being pressed and when it was released. The solution was to simply create a UIButton subclass and pass it's touches up the responder chain. MyButton.h - [code lang="cpp"] @interface MyButton : UIButton { } - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; @end [/code] MyButton.m - [code lang="cpp"] #import "BrakeButton.h" @implementation BrakeButton - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.nextResponder touchesBegan:touches withEvent:event]; // This is where the touch is passed on } - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [self.nextResponder touchesEnded:touches withEvent:event]; // This is where the touch is passed on } @end [/code] Then in your view controller be sure to import your new class: [code lang="cpp"] #import "MyButton.h" [/code] Then you simply implement the touchesBegan and touchesEnded methods. [code lang="cpp"] -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"Button Touches began"); } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"Button Touches ended"); } [/code] So what is happening here? The main point to understand is that UIButtons are in actuality just UIViews. So they inherit from UIViews giving them the ability to access UIView-like functionality. (touchesBegan, touchesEnded, etc...) I've seen many folks banging their heads against the wall trying to figure out how to capture / intercept button touches so I apparently it's a pretty common problem. Let me know if you find this helpful.

Using the UIImagePickerController in an iPhone OS v2.2.1 app & On A OS v3.0 Device

I recently wrote an iPhone application for a client using v2.2.1 of the iPhone SDK. (The client did not want to go OS v3.0 yet.) I needed to give a user the capability of taking and uploading a picture with the built-in camera or uploading a picture from the photo library on the phone. Seems pretty straightforward so far. Yes? I thought so. It turned out to be pretty tricky but the solution was very simple and was staring me in the face. The Problem I had a UIViewController with an UIImageView setup to be used as a container for the selected image. I also had several UIButtons on the page. (One button for calling up the camera. Another button for calling up the photo library, etc...) So what should have happened is that the Camera or photo library UI would display a Modal View window the user instructing them to either take a picture or select a photo from their library. Once they did that, they would be returned to the main view and whatever photo they took or selected would appear in UIImageView as a preview. However, when I was selecting the photo the "editingInfo" NSDictionary object that was supposed to be returned would be there only about 50% of the time. The other 50% it would be returned empty. I double and triple checked my code and it was all correct. Something was zapping my dictionary object and I couldn't, for the life of me, figure out what it was. The second part to this problem was that the preview image never appeared whenever the user was grabbing their image from the camera. It worked if the photo library was the source but not if it was the camera. Very, very frustrating. Here is my original code: My .h file - [code lang="cpp"] @interface AddPhotoController : UIViewController { IBOutlet UIImageView *imageView; IBOutlet UIButton *snapNewPictureButton; IBOutlet UIButton *selectFromPhotoLibraryButton; } @property (nonatomic, retain) UIImageView *imageView; @property (nonatomic, retain) UIButton *snapNewPictureButton; @property (nonatomic, retain) UIButton * selectFromPhotoLibraryButton; [/code] My .m file - [code lang="cpp"] @implementation AddPhotoController @synthesize imageView, snapNewPictureButton, selectFromPhotoLibraryButton; - (IBAction)getCameraPicture:(id)sender { UIImagePickerController *picker = [[UIImagePickerController alloc] init]; picker.delegate = self; picker.sourceType = UIImagePickerControllerSourceTypeCamera; picker.allowsImageEditing = YES; [self presentModalViewController:picker animated:YES]; [picker release]; } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo { NSLog(@"Image Meta Info.: %@",editingInfo); UIImage *selectedImage = image; imageView.image = selectedImage; self._havePictureData = YES; [self.useThisPhotoButton setEnabled:YES]; [picker dismissModalViewControllerAnimated:YES]; } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissModalViewControllerAnimated:YES]; } [/code] Like I said before, it all seems pretty straightforward. However, there were two things wrong with the above code. #1. Deprecated Method The UIImagePickerController method, – imagePickerController:didFinishPickingImage:editingInfo:, is deprecated in v3.0 of the iPhone OS. So even though I built the app using SDK v2.2.1, because the app will be running on 3.0 devices, I needed to use the new and improved method, - imagePickerController:didFinishPickingMediaWithInfo:editingInfo, which contains FIVE pieces of really important information:
  1. UIImagePickerControllerMediaType - Which specifies the type of media that this dictionary contains.
  2. UIImagePickerControllerOriginalImage - Contains the original non-cropped image.
  3. UIImagePickerControllerEditedImage - Contains the user-edited cropped image.
  4. UIImagePickerControllerCropRect - Contains the CGRect that was applied to the original image.
  5. UIImagePickerControllerMediaURL - If the object is a movie, this object contains the URL to be used by the media player.
I simply accessed the "UIImagePickerControllerEditedImage" object from the dictionary and was off to the races. #2. Dismissing the Modal View This one was really painful as it was so simple to solve. In order for the UIImageView to be updated correctly, you must dismiss the ImagePicker modal window BEFORE you attempt to update your UIImageView. As a result of this, here is my updated method call: [code lang="cpp"] - (void) imagePickerController:(UIImagePickerController *)thePicker didFinishPickingMediaWithInfo:(NSDictionary *)imageInfo { [thePicker dismissModalViewControllerAnimated:YES]; UIImage *img = [imageInfo objectForKey:@"UIImagePickerControllerEditedImage"]; previewImage.image = nil; self.previewImage.image = img; NSData *imageData = UIImagePNGRepresentation(img); if ([imageData length] > 0) { [self archivePictureData:imageData]; self._havePictureData = YES; [self.useThisPhotoButton setEnabled:YES]; } } [/code] That was it. Hope that was helpful.

iPhone OS v3.0 Features - Lotta New Goodies

I was really excited by the news of the new iPhone OS v3.0 announced last week. Copy-and-Paste aside, there are a lot of less-talked-about features that I find pretty cool and crucial to the advancement of the platform:

Peer-to-Peer Connectivity over Bluetooth

This is a feature that I thought seemed so obvious to have baked in. With Peer-to-Peer connectivity, you are now given the ability to communicate with other local phones WITHOUT having to pair with them in order to do it. (Like you have to when you use a Bluetooth mouse or headset.) This is great for applications that would need to share pieces of data for short periods of time. Or games that share proximity for a short period of time. I'm just starting to mess with this now so I am really anxious to see what the full line of functionality that is provided for is.

In App Purchase With this feature, in my mind, the iPhone grows up. Now is when e-commerce will really begin to take off with the device. I also feel that now we will *really* begin to see some money being made in the mobile arena. In App Purchasing means that you can have fully-functional mobile e-commerce without having an entire website and all of the complexities that it involves. (Yes, I realize I am a developer of web-based e-commerce solutions. We plan to make some of that money.)

iPhone Core Data Of all the new functionality and features presented... none were more significant, (for me), then Core Data now being available on the iPhone. For those of you less geek-inclined... Core Data (http://developer.apple.com/macosx/coredata.html) is a method for managing the data model for MacOSX applications. Up until now you had to write your own methods for handling things like data relationships, the adding, updating and deletion of records.

As anyone who has had to build an application with persistent data will tell you, while it's awesome to be able to develop using SQLite and plists on the iPhone, it does not compare with being able to code against an abstraction layer that just works! So I'll take the added complexity associated with Core Data any day of the week.

In Summary I love developing for the iPhone and the Macintosh. The iPhone OS v3.0, even in BETA, is such a huge leap forward. Not just for the platform, but those who write software for the iPhone and have been waiting for Apple to enable developers to really take their applications to a level above "cute" or "neato" or niche.

The only outstanding issue I see before Apple is it's whole approval/review/rejection process for iPhone applications. As a developer, it is a really frustrating process. Their methods just seem so arbitrary. (One app gets rejected. Similar app, different developer and it get approved.) I would be willing to forego new features and frameworks in the short-term in order to give Apple more time to create a more thoughtful and predictable review process with metrics and benchmarks to boot.