Identifying pinch gesture drag gesture on iPhone? - iphone

I have a small image on a view. The view is the object process multi-touch actions. If a finger drag on the view, the image will translate its position. And if user use 2 fingers to make pinch gesture, the image will scale its size. And I do work as section code below:
//touch detect methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Touch began");
//Devide into 2 cases: 1 touch and 2 touches.
if ([touches count] == 1) {
NSLog(#"Touch began cout = 1");
currentImageCenter = focusImage.center;
UITouch *touch = [[touches allObjects] objectAtIndex:0];
previousPoint = [touch locationInView:self];
isTwoFingerTouching = FALSE;
}
else if([touches count] == 2){
NSLog(#"Touch began cout = 2");
UITouch *touch = [[touches allObjects] objectAtIndex:0];
beginFirstPoint = [touch locationInView:self];
touch = [[touches allObjects] objectAtIndex:1];
beginSecondPoint = [touch locationInView:self];
isTwoFingerTouching = TRUE;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] == 1 && isTwoFingerTouching == FALSE) {
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self];
//Calculate distance
double deltaX = currentPosition.x - previousPoint.x;
double deltaY = currentPosition.y - previousPoint.y;
NSLog(#"Touch move detect 1 touch ");
focusImage.center = CGPointMake(currentImageCenter.x+deltaX, currentImageCenter.y+deltaY);
}
else if([touches count] == 2){
NSLog(#"Touch move detect 2 touches");
CGPoint currentFirstPoint;
CGPoint currentSecondPoint;
UITouch *touch = [[touches allObjects] objectAtIndex:0];
currentFirstPoint = [touch locationInView:self];
CGPoint previousFirstPoint = [touch previousLocationInView:self];
touch = [[touches allObjects] objectAtIndex:1];
currentSecondPoint = [touch locationInView:self];
CGPoint previousSecondPoint = [touch previousLocationInView:self];
//Compare previous points with current points.
//Pinch gesture
CGFloat beginDistance = distanceBetweenPoints(previousFirstPoint, previousSecondPoint);
CGFloat currentDistance = distanceBetweenPoints(currentFirstPoint, currentSecondPoint);
if (currentDistance > 0 && beginDistance > 0) {
double scale = currentDistance/beginDistance;
NSLog(#"%f", scale);
//Rotation
CGPoint vector1 = CGPointMake(previousFirstPoint.x - previousSecondPoint.x, previousFirstPoint.y - previousSecondPoint.y);
CGPoint vector2 = CGPointMake(currentFirstPoint.x - currentSecondPoint.x, currentFirstPoint.y - currentSecondPoint.y);
//[vector1, vector2].
double zValue = vector1.x*vector2.y - vector1.y*vector2.x;
CGFloat rotateAngle = angleBetweenLines(previousFirstPoint, previousSecondPoint, currentFirstPoint, currentSecondPoint);
//zValue < 0, vector1 rotate counter-clockwise, so the angle should be negative.
if (zValue < 0) {
rotateAngle = -rotateAngle;
}
//Don't allow to zoom out if the image is too small
if (scale > 1 || focusImage.frame.size.width > 30) {
CGAffineTransform previousTransform = focusImage.transform;
CGAffineTransform mixTransform = CGAffineTransformConcat(CGAffineTransformMakeScale(scale, scale), CGAffineTransformMakeRotation(rotateAngle));
focusImage.transform = CGAffineTransformConcat(previousTransform, mixTransform);
}
}
}
}
But the problem is: I can't identify whether 1 finger or 2 fingers on screen. When I touch 2 fingers, and I move (for rotate and scale) those fingers, [touches count] in touchesMoved: method still equal 1 occasionally. Any one experienced this, please tell me how to solve my problem?

Any reason why you are not using UIGestureRecognizers? It will save you a lot of work if you just use a UIPanGestureRecognizer and a UIPinchGestureRecognizer.
In any case, iOS has always behaved like that. Unless you touch with both fingers at the exact same millisecond, you will detect a one finger touch first then detect two finger touch. You need to employ some sort of a cancelling mechanism in your code if you still want to go the -touchesBegan..., -touchesMoved..., etc., method.

Related

Content updating slow on screen though FPS is 60

I am loading a menu in which i have 7 layers through which i swipe. On first load the swiping is smooth at 60fps, but i then pop the scene and push it again, the swiping is very slow, though the app displays 60fps, and the ccTouchesMoved is called roughly at the same interval as on the first load.
On each layer, i add some statical CCSprites and a CCMenu.
All items are deallocated, and i am testing the app on ipad 3 (so speed isn't quite an issue).
I presume that the cause is more on the settings part and would like to find a solution for it. Here are my touch methods, if it helps:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
//Swipe Detection Part 1
firstTouch = location;
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allTouches = [event allTouches];
UITouch * touch = [[allTouches allObjects] objectAtIndex:0];
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CGSize winSize = [[CCDirector sharedDirector] winSize];
for(int i=0;i<[layerList count];i++)
{
[[layerList objectAtIndex:i] setPosition:CGPointMake((i-currentLayer)*winSize.width + (location.x - firstTouch.x),0) ];
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allTouches = [event allTouches];
UITouch * touch = [[allTouches allObjects] objectAtIndex:0];
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
//Swipe Detection Part 2
lastTouch = location;
//Swipe Detection Part 2
lastTouch = location;
//Minimum length of the swipe
float swipeLength = ccpDistance(firstTouch, lastTouch);
//Check if the swipe is a left swipe and long enough
if (firstTouch.x > lastTouch.x && swipeLength > 60) {
[self doStuffLeft];
}
else if (firstTouch.x < lastTouch.x && abs(swipeLength) > 60) {
[self doStuffRight];
}
}
Figured it out, [CCDirector sharedInstance] was paused.

UITouch touchesMoved Finger Direction and Speed

How can I get Speed and Direction of finger movements in touchmoved function?
I want to get the finger speed and finger direction and apply it in a UIView class direction movement and animation speed.
I read this link but I can not understand the answer, in addition it is not explaining how I can detect the direction:
UITouch movement speed detection
so far, I tried this code:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *anyTouch = [touches anyObject];
CGPoint touchLocation = [anyTouch locationInView:self.view];
//NSLog(#"touch %f", touchLocation.x);
player.center = touchLocation;
[player setNeedsDisplay];
self.previousTimestamp = event.timestamp;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
CGPoint prevLocation = [touch previousLocationInView:self.view];
CGFloat distanceFromPrevious = [self distanceBetweenPoints:location :prevLocation];
NSTimeInterval timeSincePrevious = event.timestamp - previousTimestamp;
NSLog(#"diff time %f", timeSincePrevious);
}
Direction will be determined from the values of "location" and "prevLocation" in touchesMoved. Specifically, location will contain the new point of the touch. For example:
if (location.x - prevLocation.x > 0) {
//finger touch went right
} else {
//finger touch went left
}
if (location.y - prevLocation.y > 0) {
//finger touch went upwards
} else {
//finger touch went downwards
}
Now touchesMoved will get called many times for a given finger movement. It will be key to your code to compare an initial value when the finger first touches the screen, with a value of the CGPoint when the movement is finally complete.
why not just the below as a variation on obuseme's response
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *aTouch = [touches anyObject];
CGPoint newLocation = [aTouch locationInView:self.view];
CGPoint prevLocation = [aTouch previousLocationInView:self.view];
if (newLocation.x > prevLocation.x) {
//finger touch went right
} else {
//finger touch went left
}
if (newLocation.y > prevLocation.y) {
//finger touch went upwards
} else {
//finger touch went downwards
}
}

Restrict position of imageView within other imageview in iphone

I have 2 image views namely mainImageView & smallImageView. My smallImageView is added as subview to mainImageView. My smallImageView is draggable (i.e moving around the view). I want to restrict the movement of smallImageView within mainImageView (my smallImageView should not go outside of mainImageView).My smallImageView contains circle as image. Here is my code
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
smallImageView.center = location;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if (location.x < mainImageView.frame.origin.x || location.y < mainImageView.frame.origin.y) {
[self touchesBegan:touches withEvent:event];
}
}
How can I fixed this problem. Thanks.
In both touch methods,
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if(CGRectContainsPoint(mainImageView.frame, location)) {
smallImageView.center = location;
}
The disadvantage to this approach is that the small view moves to wherever you touch even if your touch doesn't start on the small view.
If you want to avoid this, set a 'dragging' boolean state only if your touchesBegan starts in the small view, and only respond to the touchesMoved if the dragging boolean is YES. Set dragging to NO on touchesEnded.
EDIT: Main view is a circle.
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
CGFloat xdifference = mainImageView.center.x - location.x;
CGFloat ydifference = mainImageView.center.y - location.y;
CGFloat distance = sqrt(xdifference * xdifference + ydifference * ydifference);
CGFloat radius = mainImageView.frame.size.width / 2;
if(distance < radius) {
smallImageView.center = location;
}

Drag View from the point you touch it iPhone

I know how to drag a view or object:
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if( [touch view] == ViewMain)
{
CGPoint location = [touch locationInView:self.view];
ViewMain.center = location;
}
}
but I would like to start dragging that view or image from the point where I touched it. for example if I drag (I highlighted the view and placed blue on the cursor):
the moment I start dragging if I drag the mouse 20 px to the right for example then I would like the view to also drag 20 px instead of:
maybe I have to change the center of the view to the point where I fist touch it. How could I do that?
In other words I would like to do something like when you drag the apps on the iPad:
float startX = 0;
float startY = 0;
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if( [touch view] == ViewMain)
{
CGPoint location = [touch locationInView:self.view];
startX = location.x - ViewMain.center.x;
startY = ViewMain.center.y;
}
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if( [touch view] == ViewMain)
{
CGPoint location = [touch locationInView:self.view];
location.x =location.x - startX;
location.y = startY;
ViewMain.center = location;
}
}

Implementing touch-based rotation in cocoa touch

I am wondering what is the best way to implement rotation-based dragging movements in my iPhone application.
I have a UIView that I wish to rotate around its centre, when the users finger is touch the view and they move it. Think of it like a dial that needs to be adjusted with the finger.
The basic question comes down to:
1) Should I remember the initial angle and transform when touchesBegan is called, and then every time touchesMoved is called apply a new transform to the view based on the current position of the finger, e.g., something like:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self]; //current position of touch
if (([touch view] == self)
&& [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS //middle is centre of view
&& [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //will be rotation gesture
//remember state of view at beginning of touch
CGPoint top = CGPointMake(self.middle.x, 0);
self.initialTouch = currentPoint;
self.initialAngle = angleBetweenLines(self.middle, top, self.middle, currentPoint);
self.initialTransform = self.transform;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self]; //current position of touch
if (([touch view] == self)
&& [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS
&& [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //a rotation gesture
//rotate tile
float newAngle = angleBetweenLines(self.middle, CGPointMake(self.middle.x, 0), self.middle, currentPoint); //touch angle
float angleDif = newAngle - self.initialAngle; //work out dif between angle at beginning of touch and now.
CGAffineTransform newTransform = CGAffineTransformRotate(self.initialTransform, angleDif); //create new transform
self.transform = newTransform; //apply transform.
}
OR
2) Should I simply remember the last known position/angle, and rotate the view based on the difference in angle between that and now, e.g.,:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self]; //current position of touch
if (([touch view] == self)
&& [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS
&& [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //will be rotation gesture
//remember state of view at beginning of touch
CGPoint top = CGPointMake(self.middle.x, 0);
self.lastTouch = currentPoint;
self.lastAngle = angleBetweenLines(self.middle, top, self.middle, currentPoint);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self]; //current position of touch
if (([touch view] == self)
&& [Utility getDistance:currentPoint toPoint:middle] <= ROTATE_RADIUS
&& [Utility getDistance:currentPoint toPoint:middle] >= MOVE_RADIUS) { //a rotation gesture
//rotate tile
float newAngle = angleBetweenLines(self.middle, CGPointMake(self.middle.x, 0), self.middle, currentPoint); //touch angle
float angleDif = newAngle - self.lastAngle; //work out dif between angle at beginning of touch and now.
CGAffineTransform newTransform = CGAffineTransformRotate(self.transform, angleDif); //create new transform
self.transform = newTransform; //apply transform.
self.lastTouch = currentPoint;
self.lastAngle = newAngle;
}
The second option makes more sense to me, but it is not giving very pleasing results (jaggy updates and non-smooth rotations). Which way is best (if any), in terms of performance?
Cheers!
It is actually much simpler than what you have tried.
You need three data points:
The origin of your view.
The location of the current touch
The location of the previous touch
The Touch object passed to you actually contains the last touch location. So you don't need to keep track of it.
All you have to do is calculate the angle between two lines:
Origin to Current Touch
Origin to Previous Touch
Then convert that to radians and use that in your CGAffineTransformRotate(). Do that all in your touchesMoved handler.
Here is a function to calculate what you need just that:
static inline CGFloat angleBetweenLinesInRadians(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;
CGFloat line1Slope = (line1End.y - line1Start.y) / (line1End.x - line1Start.x);
CGFloat line2Slope = (line2End.y - line2Start.y) / (line2End.x - line2Start.x);
CGFloat degs = acosf(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
return (line2Slope > line1Slope) ? degs : -degs;
}
Courtesy of Jeff LaMarche at:
http://iphonedevelopment.blogspot.com/2009/12/better-two-finger-rotate-gesture.html
Example:
UITouch *touch = [touches anyObject];
CGPoint origin = [view center];
CGFloat angle = angleBetweenLinesInRadians(origin, [touch previousLocationInView:self.superview.superview], origin, [touch locationInView:self.superview.superview]);
Have you considered using UIRotationGestureRecognizer? Seems like that has the logic already baked in, and might make things simpler.