To get started, we have to
1. Create a new project by selecting “Windows-based Application”
2. Create a new UIView using IB and name it ImgView.
3. Create a new file in XCode by selecting File -> New File -> UIViewController sub class and name it “ImgViewController”.
4. In IB, select File’s owner object of the “ImgView” nib file and select Tools -> Identity Inspector. Under Class Identity select “ImgViewContoller” as the class.
5. Select Tools – > Connections Inspector and create a connection from the view property to the view object in IB.
6. Place a UIImageView on the view we just created.
7. Add a image to the resource folder, to be used by the UIImageView.
8. Select the Image and select Tools -> Attributes Inspector in IB. From the Image drop down select your image.
9. Set the Mode to Center.
10. Un-check “User Interaction Enabled” and “Multiple Touch”.
11. Select the view and select “Multiple Touch” in the Attributes Inspector. Note that “User Interaction Enabled” should already be checked, if not check it.
Using the last two settings all the touches events will be sent to ImgViewController. Since we need to control the image, we need a variable of type UIImageView. This is how the header file of “MultiTouchTutorialAppDelegate” will look like.
#import <UIKit/UIKit.h>>
@class ImgViewController;
@interface MultiTouchTutorialAppDelegate : NSObject {
UIWindow *window;
ImgViewController *ivController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
Using IB, select File’s owner object and open Tools -> Connections Inspector. Create a connection from the imgView to the UIImageView placed on the view. Now we will be able to control the UIImageView from code.
Now we need to add the view “ImgView” as a subview in applicationDidFinishLaunching method. This is how the source code will look like
- (void)applicationDidFinishLaunching:(UIApplication *)application {
ivController = [[ImgViewController alloc] initWithNibName:@"ImgView" bundle:[NSBundle mainBundle]];
[window addSubview:[ivController view]];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
Click on Ctrl+return to run the application in simulator. You should see the image loaded in the simulator.
Right now the application does not respond to any events, but all we have to do is implement some methods. Let’s implement the touchesEnded method which will be called when touches are ended.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
In this method we will respond to a single tap and double tap. On single tap we will zoom out the image and on double tap we will set the mode to “Center”, which is doing the same as in step 9.
This is how the code looks like
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
//Get all the touches.
NSSet *allTouches = [event allTouches];
//Number of touches on the screen
switch ([allTouches count])
{
case 1:
{
//Get the first touch.
UITouch *touch = [[allTouches allObjects] objectAtIndex:0];
switch([touch tapCount])
{
case 1://Single tap
imgView.contentMode = UIViewContentModeScaleAspectFit;
break;
case 2://Double tap.
imgView.contentMode = UIViewContentModeCenter;
break;
}
}
break;
}
}
We get all the touches from the event into allTouches variable. We thenfind out how many fingers were touching the screen, since in our case we only want to zoom in and out, it seems normal to assume that the user will only be using one finger. We then find out how many tocuhs are touching the screen by calling the count method of allTouches variable.
If there is only one finger touching the screen, we get the Touch object at index zero and find out the tap count of that object. If the tap count is one we will zoom out the image and if it is two we are going to zoom in. We zoom out the image by setting the contentMode to UIViewContentModeScaleAspectFit and zoom in the image by setting it to UIViewContentModeCenter.
If you’re pulling in web data such as images that doesn’t change but needs to be viewed multiple times, you’ll want to cache it. Caching images dramatically improves the performance of scrolling tables and other views.
Note: this only works for JPG and PNG images. If you have suggestions or find bugs, let me know.
Utilities.h:
Code:
#import Foundation/Foundation.h
@interface Utilities : NSObject {
}
// Methods
- (void) cacheImage: (NSString *) ImageURLString;
- (UIImage *) getCachedImage: (NSString *) ImageURLString;
- (UIImage *) roundCorners: (UIImage*) img;
@end
Utilities.m
Code:
#import "Utilities.h"
#define TMP NSTemporaryDirectory()
@implementation Utilities
Function to actually cache the image – checks to see if one with the same name already exists
Code:
- (void) cacheImage: (NSString *) ImageURLString
{
NSURL *ImageURL = [NSURL URLWithString: ImageURLString];
// Generate a unique path to a resource representing the image you want
NSString *filename = [[something unique, perhaps the image name]];
NSString *uniquePath = [TMP stringByAppendingPathComponent: filename];
// Check for file existence
if(![[NSFileManager defaultManager] fileExistsAtPath: uniquePath])
{
// The file doesn't exist, we should get a copy of it
// Fetch image
NSData *data = [[NSData alloc] initWithContentsOfURL: ImageURL];
UIImage *image = [[UIImage alloc] initWithData: data];
// Do we want to round the corners?
image = [self roundCorners: image];
// Is it PNG or JPG/JPEG?
// Running the image representation function writes the data from the image to a file
if([ImageURLString rangeOfString: @".png" options: NSCaseInsensitiveSearch].location != NSNotFound)
{
[UIImagePNGRepresentation(image) writeToFile: uniquePath atomically: YES];
}
else if(
[ImageURLString rangeOfString: @".jpg" options: NSCaseInsensitiveSearch].location != NSNotFound ||
[ImageURLString rangeOfString: @".jpeg" options: NSCaseInsensitiveSearch].location != NSNotFound
)
{
[UIImageJPEGRepresentation(image, 100) writeToFile: uniquePath atomically: YES];
}
}
}
Retrieve a cached file:
Code:
- (UIImage *) getCachedImage: (NSString *) ImageURLString
{
NSString *filename = [[something unique, perhaps the image name]];
NSString *uniquePath = [TMP stringByAppendingPathComponent: filename];
UIImage *image;
// Check for a cached version
if([[NSFileManager defaultManager] fileExistsAtPath: uniquePath])
{
image = [UIImage imageWithContentsOfFile: uniquePath]; // this is the cached image
}
else
{
// get a new one
[self cacheImage: ImageURLString];
image = [UIImage imageWithContentsOfFile: uniquePath];
}
return image;
}
These two functions are just to add rounded corners to an image
Code:
static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight)
{
float fw, fh;
if (ovalWidth == 0 || ovalHeight == 0)
{
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM (context, ovalWidth, ovalHeight);
fw = CGRectGetWidth (rect) / ovalWidth;
fh = CGRectGetHeight (rect) / ovalHeight;
CGContextMoveToPoint(context, fw, fh/2);
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
CGContextClosePath(context);
CGContextRestoreGState(context);
}
- (UIImage *) roundCorners: (UIImage*) img
{
int w = img.size.width;
int h = img.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
addRoundedRectToPath(context, rect, 5, 5);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
[img release];
return [UIImage imageWithCGImage:imageMasked];
}
@end