I have a UIView with custom drawRect() method, used for some drawing.
I use UIPinchGestureRecognizer to achieve zooming effect.
This is action method:
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer
{
//NSLog(#"Pinch gesture recognized");
CGPoint touchOrigin = [recognizer locationInView:self];
_currentScaleOrigin = touchOrigin;
if (recognizer.state == UIGestureRecognizerStateEnded)
{
NSLog(#"%16# x = %f, y = %f", #"Pinch end:", touchOrigin.x, touchOrigin.y);
return;
}
if (recognizer.state == UIGestureRecognizerStateChanged)
{
//NSLog(#"scale = %f", recognizer.scale);
NSLog(#"%16# x = %f, y = %f", #"Pinch origin:", touchOrigin.x, touchOrigin.y);
// scale has at start a value of 1.0 and increases as fingers moves away from each other
_currentScaleLevel += recognizer.scale - 1;
[self setNeedsDisplay]; // call drawRect for redrawing at current scale level
}
recognizer.scale = 1;
}
When receiving UIGestureRecognizerStateEnded message i get some strange offset to coordinates:
Pinch origin: x = 358.000000, y = 630.000000
Pinch origin: x = 355.000000, y = 627.000000
Pinch origin: x = 353.000000, y = 625.000000
Pinch origin: x = 351.000000, y = 624.000000
Pinch origin: x = 351.000000, y = 623.000000
Pinch origin: x = 350.000000, y = 622.000000
Pinch origin: x = 349.000000, y = 622.000000
Pinch origin: x = 349.000000, y = 622.000000
Pinch end: x = 315.000000, y = 750.000000
... and translation for free :) which i dont need.
I dont know from where translation comes from.
How to disable this translation?
Solved it.
When receiving UIGestureRecognizerStateEnded i was assigning a point of last of two fingers to my _currentScaleOrigin which is for performing scaling transformation.
I didnt know that UIPinchGestureRecognizer returns a CGPoint of last finger position on screen.
Here is whole method code:
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer
{
#if 1
static CGPoint point_0;
static CGPoint point_1;
if ([recognizer numberOfTouches] > 1)
{
point_0 = [recognizer locationOfTouch:0 inView:self];
point_1 = [recognizer locationOfTouch:1 inView:self];
NSLog(#"pinch loc 0: (%f, %f)", point_0.x , point_0.y);
NSLog(#"pinch loc 1: (%f, %f)", point_1.x , point_1.y);
} else if ([recognizer numberOfTouches] == 1) {
point_0 = [recognizer locationOfTouch:0 inView:self];
NSLog(#"pinch loc 0: (%f, %f)", point_0.x , point_0.y);
}
#endif
switch (recognizer.state)
{
case UIGestureRecognizerStateBegan:
{
CGPoint pointBegin = [recognizer locationInView:self];
NSLog(#"%16# x = %f, y = %f", #"Pinch start:", pointBegin.x, pointBegin.y);
} break;
case UIGestureRecognizerStateChanged:
{
_currentScaleOrigin = [recognizer locationInView:self];
//NSLog(#"scale = %f", recognizer.scale);
NSLog(#"%16# x = %f, y = %f", #"Pinch origin:", _currentScaleOrigin.x, _currentScaleOrigin.y);
// scale has at start a value of 1.0 and increases as fingers moves away from each other
_currentScaleLevel += recognizer.scale - 1;
// call drawRect for redrawing at current scale level
[self setNeedsDisplay];
} break;
case UIGestureRecognizerStateEnded:
{
CGPoint pointEnded = [recognizer locationInView:self];
NSLog(#"%16# x = %f, y = %f", #"Pinch end:", pointEnded.x, pointEnded.y);
//return;
} break;
default :
{
NSLog(#"other state");
}
}
recognizer.scale = 1;
}
Related
I set Google Map in view in UIScrollView. everything OK but i can not pinch my map. How to fix it?
Salaam Reza jaan.
In your case you can use something like this. Cheers.
Edit: this is a better way to do it. Behbakht shid, direh inja :P
- (void) scaleSelfWith:(UIPinchGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer numberOfTouches] >1) {
//getting width and height between gestureCenter and one of my finger
float x = [gestureRecognizer locationInView:self].x - [gestureRecognizer locationOfTouch:1 inView:self].x;
if (x<0) {
x *= -1;
}
float y = [gestureRecognizer locationInView:self].y - [gestureRecognizer locationOfTouch:1 inView:self].y;
if (y<0) {
y *= -1;
}
//set Border
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
xDis = self.bounds.size.width - x*2;
yDis = self.bounds.size.height - y*2;
}
//double size cause x and y is just the way from the middle to my finger
float width = x*2+xDis;
if (width < 1) {
width = 1;
}
float height = y*2+yDis;
if (height < 1) {
height = 1;
}
self.bounds = CGRectMake(self.bounds.origin.x , self.bounds.origin.y , width, height);
[gestureRecognizer setScale:1];
[[self layer] setBorderWidth:2.f];
}
}
I've got an UIImageView with a pan gesture recognizer, which I move and rotate based on user action.
When user lifts the finger, I want it to be animated back to its original position, here's my code:
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
startX = recognizer.view.center.x;
startY = recognizer.view.center.y;
startRotation = atan2(recognizer.view.transform.b, recognizer.view.transform.a);
NSLog(#"Start Position %f %f %f", startX, startY, startRotation);
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
float distance = (startX - recognizer.view.center.x) / DISTANCE_TO_ACCEPT;
CGPoint center = recognizer.view.center;
// animate back
[UIView animateWithDuration:0.5 animations:^{
NSLog(#"Position %f %f %f", recognizer.view.center.x, recognizer.view.center.y, atan2(recognizer.view.transform.b, recognizer.view.transform.a));
NSLog(#"Destination %f %f %f", startX, startY, startRotation);
NSLog(#"Translation %f %f %f", startX - center.x, startY - center.y, (startRotation - atan2(recognizer.view.transform.b, recognizer.view.transform.a)));
recognizer.view.transform = CGAffineTransformRotate(CGAffineTransformTranslate(recognizer.view.transform, startX - center.x, startY - center.y), (CGFloat) (startRotation - atan2(recognizer.view.transform.b, recognizer.view.transform.a)));
}];
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
// move image
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
// rotate image
float distance = (startX - recognizer.view.center.x) / DISTANCE_TO_ACCEPT;
// cap distance
if (distance > 1) {
distance = 1;
} else if (distance < -1) {
distance = -1;
}
double rotation = 15 - startRotation;
rotation = rotation * distance;
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, (CGFloat) ((rotation * M_PI / 180) - atan2(recognizer.view.transform.b, recognizer.view.transform.a)));
}
}
But it never goes to the original position. It always has some kind of offset. Further movement of the image view only makes it worse.
It looks like the position is not modified during the back animation, because when I pan the image again, the start position and rotation is equal to the position and rotation it had at the end of previous movement [even though on screen the image position and rotation has changed].
What am I doing wrong here?
Thanks
Try something like this in UIGestureRecognizerStateEnded state:
recognizer.view.transform = CGAffineTransformIdentity;
recognizer.view.center = CGPointMake(startX,startY);
There is a UIView A. I put a icon on view A and try to use pan gesture to scale and rotate this view A. The scale function works fine but I can't make rotation work. The code is as following. Any help will be appreciated. thanks
- (void)scaleAndRotateWatermark:(UIPanGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateChanged
|| gesture.state == UIGestureRecognizerStateEnded)
{
UIView *child = gesture.view;
UIView *view = child.superview;
CGPoint translatedPoint = [gesture translationInView:view];
CGPoint originCenter = child.center;
CGPoint newCenter = CGPointMake(child.centerX+translatedPoint.x, child.centerY+translatedPoint.y);
float origX = originCenter.x;
float origY = originCenter.y;
float newX = newCenter.x;
float newY = newCenter.y;
float originDis = (origX*origX) + (origY*origY);
float newDis = (newX*newX) + (newY*newY);
float scale = newDis/originDis;
view.transform = CGAffineTransformScale(view.transform, scale, scale);
// rotation calulate here
// need your help
// end of rotation
[guesture setTranslation:CGPointZero inView:_videoPreviewLayer];
}
}
I want to zoom in out a CCNode by pinching and panning the screen. The node has a background which is very large but the portionof it shown on the screen. That node also contains other sprites.
What I have done by now is that first I register UIPinchGestureRecognizer
UIPinchGestureRecognizer * pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinchFrom:)];
[[[CCDirector sharedDirector] view] addGestureRecognizer: pinchRecognizer];
-(void)handlePinchFrom:(UIPinchGestureRecognizer *) pinch
{
if(pinch.state == UIGestureRecognizerStateEnded) {
prevScale = 1;
}
else {
CGFloat dscale = [self scale] - prevScale + pinch.scale;
if(dscale > 0)
{
deltaScale = dscale;
}
CGAffineTransform transform = CGAffineTransformScale(pinch.view.transform, deltaScale, deltaScale);
[pinch.view setTransform: transform];
// [_contentNode setScale:deltaScale];
prevScale = pinch.scale;
}
}
The problem is that it scalw whole UIView not the CCNode. I have also tried to by setting the scale of my _contentNode.
**EDIT
I ave also tried this
- (void)handlePinchGesture:(UIPinchGestureRecognizer*)aPinchGestureRecognizer
{
if (pinch.state == UIGestureRecognizerStateBegan || pinch.state == UIGestureRecognizerStateChanged) {
CGPoint midpoint = [pinch locationInView:[CCDirector sharedDirector].view];
CGSize winSize = [CCDirector sharedDirector].viewSize;
float x = midpoint.x/winSize.width;
float y = midpoint.y/winSize.height;
_contentNode.anchorPoint = CGPointMake(x, y);
float scale = [pinch scale];
_contentNode.scale *= scale;
pinch.scale = 1;
}
}
But it zoom from the bottom left of the screen.
I had the same problem. I use CCScrollView, that contains CCNode that larger than device screen. I want scroll and zoom it, but node shouldnt scroll out of screen, and scale smaller than screen. So, i create my subclass of CCScrollView, where i handle pinch. It has some strange glitches, but it works fine at all.
When pinch began i set anchor point of my node to pinch center on node space. Then i need change position of my node proportional to shift of anchor point, so moving anchor point doesn't change nodes location on view:
- (void)handlePinch:(UIPinchGestureRecognizer*)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded) {
_previousScale = self.contentNode.scale;
}
else if (recognizer.state == UIGestureRecognizerStateBegan) {
float X = [recognizer locationInNode:self.contentNode].x / self.contentNode.contentSize.width;
float Y = [recognizer locationInNode:self.contentNode].y / self.contentNode.contentSize.height;
float positionX = self.contentNode.position.x + self.contentNode.boundingBox.size.width * (X - self.contentNode.anchorPoint.x);
float positionY = self.contentNode.position.y + self.contentNode.boundingBox.size.height * (Y - self.contentNode.anchorPoint.y);
self.contentNode.anchorPoint = ccp(X, Y);
self.contentNode.position = ccp(positionX, positionY);
}
else {
CGFloat scale = _previousScale * recognizer.scale;
if (scale >= maxScale) {
self.contentNode.scale = maxScale;
}
else if (scale <= [self minScale]) {
self.contentNode.scale = [self minScale];
}
else {
self.contentNode.scale = scale;
}
}
}
Also i need change CCScrollView min and max scroll, so my node never scroll out of view. Default anchor point is (0,1), so i need shift min and max scroll proportional to the new anchor point.
- (float) maxScrollX
{
if (!self.contentNode) return 0;
float maxScroll = self.contentNode.boundingBox.size.width - self.contentSizeInPoints.width;
if (maxScroll < 0) maxScroll = 0;
return maxScroll - self.contentNode.boundingBox.size.width * self.contentNode.anchorPoint.x;
}
- (float) maxScrollY
{
if (!self.contentNode) return 0;
float maxScroll = self.contentNode.boundingBox.size.height - self.contentSizeInPoints.height;
if (maxScroll < 0) maxScroll = 0;
return maxScroll - self.contentNode.boundingBox.size.height * (1 - self.contentNode.anchorPoint.y);
}
- (float) minScrollX
{
float minScroll = [super minScrollX];
return minScroll - self.contentNode.boundingBox.size.width * self.contentNode.anchorPoint.x;
}
- (float) minScrollY
{
float minScroll = [super minScrollY];
return minScroll - self.contentNode.boundingBox.size.height * (1 - self.contentNode.anchorPoint.y);
}
UIGestureRecognizerStateEnded doesn't have locationInNode: method, so i added it by category. It just return touch location on node space:
#import "UIGestureRecognizer+locationInNode.h"
#implementation UIGestureRecognizer (locationInNode)
- (CGPoint) locationInNode:(CCNode*) node
{
CCDirector* dir = [CCDirector sharedDirector];
CGPoint touchLocation = [self locationInView: [self view]];
touchLocation = [dir convertToGL: touchLocation];
return [node convertToNodeSpace:touchLocation];
}
- (CGPoint) locationInWorld
{
CCDirector* dir = [CCDirector sharedDirector];
CGPoint touchLocation = [self locationInView: [self view]];
return [dir convertToGL: touchLocation];
}
#end
I'm trying to use UIPanGestureRecognizer to flip an UIView. I'm using below code to handle flipping the view,
- (void)handlePan:(UIPanGestureRecognizer*)gesture{
CGPoint translation = [gesture translationInView:self.view];
NSLog(#"PAN X VALUE %f", translation.x );
double percentageOfWidth = translation.x / (gesture.view.frame.size.width / 2);
float angle = (percentageOfWidth * 100) * M_PI_2 / 180.0f;
CALayer *layer = testView.layer;
CATransform3D flipTransform = CATransform3DIdentity;
flipTransform.m34 = -0.002f;
flipTransform = CATransform3DRotate(flipTransform, angle, 0.0f, 1.0f, 0.0f);
layer.transform = flipTransform;
}
My problem is when i pan, sometimes there are some quick jumpings happen, I believe thats because translation.x(PAN X VALUE) value jumps from few points ahead, In my case i need it to be very smooth.
Any help greatly appreciated.
Thanks in advance.
You can use the gestureRecognizerShouldBegin method, which u can limit the UIPanGestureRecognizer sensitivity.
Example:
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint translation = [panGestureRecognizer translationInView:self.view];
return fabs(translation.y) < fabs(translation.x);
}
Here's how I solved this for a cube rotation - just take the amount dragged and divide:
- (void)panHandle:(UIPanGestureRecognizer*)recognizer;
{
if ([recognizer state] == UIGestureRecognizerStateBegan)
{
CGPoint translation = [recognizer translationInView:[self superview]];
_startingX = translation.x;
}
else if ([recognizer state] == UIGestureRecognizerStateChanged)
{
CGPoint translation = [recognizer translationInView:[self superview]];
CGFloat angle = -(_startingX - translation.x) / 4;
//Now do translation with angle
_transitionContainer.layer.sublayerTransform = [self->_currentMetrics asTransformWithAngle:angle];
}
else if ([recognizer state] == UIGestureRecognizerStateEnded)
{
[self transitionOrReturnToOrigin];
}
}