**Note: I am new to Objective-C and any tips on how to code this better are greatly appreciated
OK, here is what I have in my "GameLayer.m":
/** Returns the minimum allowable scale factor for the gameViewLayer */
//This should probably be a property?
-(float) minScale
{
CGSize s = [[CCDirector sharedDirector] winSize];
return s.width / self.contentSize.width;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches]; //this gets not just the touch
//that triggered the event, but all the touches. You need this!
switch ([allTouches count]) {
case 1: { //Single touch
//Get the first touch.
UITouch *touch = [[allTouches allObjects] objectAtIndex:0];
switch ([touch tapCount])
{
case 1: case 2: //Single or Double Tap.
{
[self initDragZoomValues:allTouches];
} break;
}
} break;
case 2: { //Double Touch <-- this almost never happens, btw.
//NSLog(@"Double Touch");
} break;
default:
break;
}
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
switch ([allTouches count])
{
case 1: {
//NSLog(@"One touch moved");
// drag method
[self dragLayer:[touches anyObject]];
} break;
case 2: {
//The image is being zoomed in or out. ) mTargetLayer
//NSLog(@"Two touch moved");
[self doubleDragZoomLayer:allTouches];
} break;
}
}
//Calculates position of layer based on original location, two-finger midpoint, original zoom and end zoom
- (CGPoint)adjustZoomPosition:(CGPoint)fromPoint anchoredOn:(CGPoint)anchorPoint fromZoom:(float)fromZoom toZoom:(float)toZoom {
float newX, newY;
newX = anchorPoint.x - ((anchorPoint.x-fromPoint.x)*toZoom/fromZoom);
newY = anchorPoint.y - ((anchorPoint.y-fromPoint.y)*toZoom/fromZoom);
return ccp(newX,newY);
}
//Adjusts the layer position so that the viewport doesn't go outside the contentSize
- (CGPoint)adjustPositionInbounds:(CGPoint)currentPoint {
float newX, newY, zoomWidth, zoomHeight;
BOOL changedX, changedY;
changedX = NO;
changedY = NO;
CGSize size = [[CCDirector sharedDirector] winSize];
zoomWidth = self.contentSize.width*self.scale;
zoomHeight = self.contentSize.height*self.scale;
if( currentPoint.x < -zoomWidth*(1-self.anchorPoint.x)+size.width){
newX = -zoomWidth*(1-self.anchorPoint.x)+size.width;
changedX = YES;
}
if( currentPoint.x > zoomWidth*(self.anchorPoint.x) ){
newX = zoomWidth*(self.anchorPoint.x);
changedX = YES;
}
if( currentPoint.y < -zoomHeight*(1-self.anchorPoint.y)+size.height) {
newY = -zoomHeight*(1-self.anchorPoint.y)+size.height;
changedY = YES;
}
if( currentPoint.y > zoomHeight*(self.anchorPoint.y)) {
newY = zoomHeight*(self.anchorPoint.x);
changedY = YES;
}
if (changedX | changedY) {
if(changedX!=YES) newX = currentPoint.x;
if(changedY!=YES) newY = currentPoint.y;
return ccp(newX,newY);
} else {
return currentPoint;
}
}
//Sets up the dragging and zooming initial variables so they are reset between touches.
-(void)initDragZoomValues:(NSSet *)allTouches {
oneTouchStart = ccp(0.0f, 0.0f);
//NSSet *allTouches = [event allTouches];
if ([allTouches count] == 2) {
//Track the initial distance between two fingers.
UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
CGPoint firstTouch = [touch1 locationInView:[touch1 view]];
CGPoint secondTouch = [touch2 locationInView:[touch2 view]];
twoTouchStart = ccp(0.0f,0.0f);
initialDistance = sqrt(pow(firstTouch.x - secondTouch.x, 2.0f) + pow(firstTouch.y - secondTouch.y, 2.0f));
}
}
//Drags the layer when a single touch moves
-(void)dragLayer:(UITouch *)dragTouch{
CGPoint location = [dragTouch locationInView: [dragTouch view]];
CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];
float newCenterX, newCenterY;
//set the start values if they haven't been set yet
if (oneTouchStart.x == 0.0f){oneTouchStart.x=convertedLocation.x;}
if (oneTouchStart.y == 0.0f){oneTouchStart.y=convertedLocation.y;}
//determine new layer position
newCenterX = self.position.x+(convertedLocation.x-oneTouchStart.x);
newCenterY = self.position.y+(convertedLocation.y-oneTouchStart.y);
[self setPosition:[self adjustPositionInbounds:ccp(newCenterX, newCenterY)]];
//set new location for next touchesmoved
oneTouchStart = ccp(convertedLocation.x,convertedLocation.y);
}
//Drags/Zooms the layer when double touch moves
-(void)doubleDragZoomLayer:(NSSet *)allTouches {
UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
CGPoint firstTouch = [touch1 locationInView:[touch1 view]];
CGPoint secondTouch = [touch2 locationInView:[touch2 view]];
CGPoint twoTouchMoved = [[CCDirector sharedDirector] convertToGL:ccp((firstTouch.x+secondTouch.x)/2,(firstTouch.y+secondTouch.y)/2)];
if (twoTouchStart.x == 0) {twoTouchStart.x = twoTouchMoved.x;}
if (twoTouchStart.y == 0) {twoTouchStart.y = twoTouchMoved.y;}
CGPoint newMidPoint = ccp(self.position.x+(twoTouchMoved.x-twoTouchStart.x),self.position.y+(twoTouchMoved.y-twoTouchStart.y));
twoTouchStart = twoTouchMoved;
CGFloat finalDistance = sqrt(pow(firstTouch.x - secondTouch.x, 2.0f) + pow(firstTouch.y - secondTouch.y, 2.0f));
float startZoom, endZoom;
//Check if zoom in or zoom out.
if(initialDistance > finalDistance*1.01f) { //added the multiplier to get rid of jitter when double-dragging
// zoom out
if (self.scale > self.minScale) {
startZoom = self.scale;
endZoom = startZoom -startZoom*0.05f;
if (endZoom < self.minScale) endZoom = self.minScale;
self.scale = endZoom;
CGPoint newPoint = [self adjustZoomPosition:self.position anchoredOn:twoTouchMoved fromZoom:startZoom toZoom:endZoom];
[self setPosition:[self adjustPositionInbounds:newPoint]];
}
}
else if(initialDistance < finalDistance*0.99f) { //added the multiplier to get rid of jitter when double-dragging
// zoom in
if (self.scale < 1.0f) {
startZoom=self.scale;
endZoom = startZoom +startZoom *0.05f;
if (endZoom > 1.0f) endZoom = 1.0f;
self.scale = endZoom;
CGPoint newPoint = [self adjustZoomPosition:self.position anchoredOn:twoTouchMoved fromZoom:startZoom toZoom:endZoom];
[self setPosition:[self adjustPositionInbounds:newPoint]];
}
}
else {
[self setPosition:[self adjustPositionInbounds:newMidPoint]];
}
initialDistance = finalDistance;
}
and here is what I have in my GameLayer.h file:
@interface GameLayer : CCLayer {
CGPoint oneTouchStart; //Initial position of single touch
CGPoint twoTouchStart; //Initial mid-point of double touch
CGFloat initialDistance; //Initial distance between double touch
}
-(float)minScale;
-(CGPoint)adjustZoomPosition:(CGPoint)fromPoint anchoredOn:(CGPoint)anchorPoint fromZoom:(float)fromZoom toZoom:(float)toZoom; //
-(CGPoint)adjustPositionInbounds:(CGPoint)currentPoint;
-(void)initDragZoomValues:(NSSet *)allTouches;
-(void)dragLayer:(UITouch *)dragTouch;
-(void)doubleDragZoomLayer:(NSSet *)allTouches;
@end
I hope that helps someone :)