App Rochester - iPhone Programming Presentation

In followup from my iPhone programming presentation I have uploaded the presentation and sample Xcode project (Xcode 4.3.2) The sample converts between temperatures in celsius and fahrenheit. The sample shows how to setup the gestures in Xcode's interface builder, rather than in Objective-C code. Using the UIPanGestureRecognizer we can allow sliding input for the temperature conversion. The only code we need to implement is the action to take in response to a detected gesture.

Download the slides and sample code:

 

Limiting UIPinchGestureRecognizer Zoom Levels

Here is how to use a UIPinchGestureRecognizer and how to limit it's zoom levels on your custom views and content. My use case was for resizing images on a custom view. I wanted to prevent very large images and to prevent very small images. Very large images have a ton of pixilation and artifacts. While very small images are hard to touch and a user cannot do anything useful with them.

Basic Math

1. We know: currentSize * scaleFactor = newSize

2. Clamp the maximum scale factor using the proportion maxScale / currentScale

3. Clamp the minimum scale factor using the proportion minScale / currentScale

The code below assumes there is an instance variable CGFloat lastScale and that a view has been set for the UIPinchGestureRecognizer.

Sample Code

#import <QuartzCore/QuartzCore.h>   // required for working with the view's layers

//....

- (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer {

if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [gestureRecognizer scale];
}

if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {

CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@"transform.scale"] floatValue];

// Constants to adjust the max/min values of zoom
const CGFloat kMaxScale = 2.0;
const CGFloat kMinScale = 1.0;

CGFloat newScale = 1 -  (lastScale - [gestureRecognizer scale]); // new scale is in the range (0-1)
newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
[gestureRecognizer view].transform = transform;

lastScale = [gestureRecognizer scale];  // Store the previous scale factor for the next pinch gesture call
}
}