I'm trying to draw a ruller which follow the touches on the screen. On the first touch, I set the first point and on all the other, the ruller follow the finger on ther screen resizing and rotating around the first point depending on where the finger is.
I tried to do this :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self.view];
self.regleFirstPoint = p;
UIImageView* img = [[UIImageView alloc] initWithImage:self.regleImg];
img.frame = CGRectMake(self.regleFirstPoint.x, self.regleFirstPoint.y, 0, self.regleImg.size.height);
[self.view addSubview:img];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self.view];
// Width
float deltaY = p.y - self.regleFirstPoint.y;
float deltaX = p.x - self.regleFirstPoint.x;
float width = sqrt((deltaX * deltaX) + (deltaY * deltaY));
// Angle
float angleInRadians = atanf(deltaY / deltaX);
float angleInDegrees = angleInRadians * 180 / M_PI; // JUST FOR INFO
NSLog(#"angle : %f / %f", angleInRadians, angleInDegrees);
// Resizing image
UIImageView* img = [self.regles lastObject];
img.frame = CGRectMake(self.regleFirstPoint.x, self.regleFirstPoint.y, width, self.regleImg.size.height/2);
img.center = self.regleFirstPoint;
CGAffineTransform transform = CGAffineTransformIdentity;
img.transform = CGAffineTransformRotate(transform, angleInRadians);
}
The ruller doesn't follow the finger correctly, I think I missed something. What's wrong with my code ?
EDIT : I also tried this after some researches :
// Resizing images
img.frame = CGRectMake(self.regleFirstPoint.x, self.regleFirstPoint.y, largeur, self.regleImg.size.height/2);
[img.layer setAnchorPoint:CGPointMake(self.regleFirstPoint.x / img.bounds.size.width, self.regleFirstPoint.y / img.bounds.size.height)];
img.transform = CGAffineTransformRotate(img.transform, angleInRadians);
This is just a question of order:
Set transform of your view to identity
Change the frame of your view
Finally, apply your transform
Here is an updated piece of code, just used an UIView instead of your image:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
// Hold first touch
self.regleFirstPoint = [touch locationInView:self.view];
// Reset view / image
_rulerView.transform = CGAffineTransformIdentity;
_rulerView.frame = CGRectMake(self.regleFirstPoint.x, self.regleFirstPoint.y, 0, CGRectGetHeight(_rulerView.frame));
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self.view];
// Compute width
float deltaX = p.x - self.regleFirstPoint.x;
float deltaY = p.y - self.regleFirstPoint.y;
float width = sqrt((deltaX * deltaX) + (deltaY * deltaY));
// Compute angle
float angleInRadians = atan2(deltaY, deltaX);
NSLog(#"angle (rad) : %f", angleInRadians);
NSLog(#"angle (deg) : %f", angleInRadians * 180 / M_PI);
// First reset the transformation to identity
_rulerView.transform = CGAffineTransformIdentity;
// Set anchor point
_rulerView.layer.anchorPoint = CGPointMake(0.0f, 0.5f);
// Resizing view / image
_rulerView.frame = CGRectMake(self.regleFirstPoint.x, self.regleFirstPoint.y, width, CGRectGetHeight(_rulerView.frame));
// Reset the layer position to the first point
_rulerView.layer.position = self.regleFirstPoint;
// Apply rotation transformation
_rulerView.transform = CGAffineTransformMakeRotation(angleInRadians);
}
Hope that helps.
Cyril
Related
I have an UIButton that I've creates programmatically. Actually it should'n be UIButton, I just need to have possibility to mark some area above the image.
So the features I need it - move object and resize it. For this i have 2 methods:
- (void) objMove:(id) sender withEvent:(UIEvent *) event
{
UIControl *control = sender;
UITouch *t = [[event allTouches] anyObject];
CGPoint pPrev = [t previousLocationInView:control];
CGPoint p = [t locationInView:control];
CGPoint center = control.center;
center.x += p.x - pPrev.x;
center.y += p.y - pPrev.y;
control.center = center;
}
- (void)objScale:(UIPinchGestureRecognizer *)recognizer
{
UIView *pinchView = recognizer.view;
CGRect bounds = pinchView.bounds;
CGPoint pinchCenter = [recognizer locationInView:pinchView];
pinchCenter.x -= CGRectGetMidX(bounds);
pinchCenter.y -= CGRectGetMidY(bounds);
CGAffineTransform transform = pinchView.transform;
transform = CGAffineTransformTranslate(transform, pinchCenter.x, pinchCenter.y);
CGFloat scale = recognizer.scale;
transform = CGAffineTransformScale(transform, scale, scale);
transform = CGAffineTransformTranslate(transform, -pinchCenter.x, -pinchCenter.y);
pinchView.transform = transform;
recognizer.scale = 1.0;
}
Scale works ok. Moving looks ok until I change the size of object - when i increase object it become moves slower than finger, and vice versa - if object smaller than original it moves faster than finger. why it works like this?
I think you should get startPoint and startCenter in
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// get startPoint and startCenter here
}
- (void) objMove:(id) sender withEvent:(UIEvent *) event
{
UIControl *control = sender;
UITouch *t = [[event allTouches] anyObject];
CGPoint p = [t locationInView:control];
startCenter.x += p.x - startPoint.x;
startCenter.y += p.y - startPoint.y;
control.center = startCenter;
}
Change your code like this, maybe it works.
Your center is current center, p is current point, pPrev is previous point.
current center adds previous point moved size is wrong.
You should get relative distance, not dynamic distance.
I am trying to drag a UITextView object with touch ! but I have a problem , I need when user touches the custom view , the touching event begins, but touching methods works only on self.view . here is my code :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touch began ");
UITouch *touch = [touches anyObject];
startingX = [touch locationInView:_captureView].x;
startingY = [touch locationInView:_captureView].y;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint currentPoint = _mText.frame.origin;
float xForView, yForView;
UITouch *touch = [touches anyObject];
float newX = [touch locationInView:_captureView].x;
float deltaX;
if(startingX > newX){
deltaX = startingX - newX;
xForView = currentPoint.x - deltaX;
} else if(newX > startingX){
deltaX = newX - startingX;
xForView = currentPoint.x + deltaX;
} else xForView = currentPoint.x;
float newY = [touch locationInView:_captureView].y;
float deltaY;
if(startingY > newY){
deltaY = startingY - newY;
yForView = currentPoint.y - deltaY;
} else if(newY > startingY){
deltaY = newY - startingY;
yForView = currentPoint.y + deltaY;
} else yForView = currentPoint.y;
CGRect newFrame = CGRectMake(xForView, yForView, _mText.frame.size.width, _mText.frame.size.height);
_mText.frame = newFrame;
startingX = newX;
startingY = newY;
}
my textView is not editable all views are userInteractionEnabled
Write your touches began method and touches moved method inside the your custom text view and try to adjust the frame.
In my app, a circle is drawn based on a users drag. e.g. a user taps, that is the center of a circle that will be drawn, and as they drag their finger, the circle will grow to that point. This works, except for some reason the center moves down and to the right as the radius of the circle grows. Why is this happening? Here is what I am trying:
#implementation CircleView{
CGPoint center;
CGPoint endPoint;
CGFloat distance;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
center = [touch locationInView:self];
[self setNeedsDisplay];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
endPoint = [touch locationInView:self];
CGFloat xDist = (endPoint.x - center.x);
CGFloat yDist = (endPoint.y - center.y);
distance = sqrt((xDist * xDist) + (yDist * yDist));
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGRect rectangle = CGRectMake(center.x,center.y,distance, distance);
CGContextAddEllipseInRect(context, rectangle);
CGContextStrokePath(context);
}
What should be happening is that center point never ever moves, and the circle should just grow. Any ideas?
CGRect rectangle = CGRectMake(center.x,center.y,distance, distance);
should be:
CGRect rectangle = CGRectMake(center.x - distance, center.y - distance, distance * 2, distance * 2);
Because in CGRectMake you have to specify the origin (and the size) of the rectangle, not the center.
CGRect rectangle = CGRectMake(center.x - distance, center.y - distance, distance * 2, distance * 2);
I am rotating imageView which is minuit hand of a clock, with
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == _imgViewMinuit)
{
NSLog(#"Touched");
_imgMinuitHand.transform = CGAffineTransformRotate(_imgMinuitHand.transform,DEGREES_RADIANS(6));
}
}
Now i want to rotate it as per my finger, clockwise/anticlockwise, with speed of my touch.
How I can achieve this ?
check this code
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[[event allTouches]anyObject];
[self transformSpinnerwithTouches:touch];
}
-(void)transformSpinnerwithTouches:(UITouch *)touchLocation
{
CGPoint touchLocationpoint = [touchLocation locationInView:self.view];
CGPoint PrevioustouchLocationpoint = [touchLocation previousLocationInView:self.view];
//origin is the respective piont from that i gonna measure the angle of the current position with respective to previous position ....
CGPoint origin;
origin.x = arrow.center.x;
origin.y = arrow.center.y;
CGPoint previousDifference = [self vectorFromPoint:origin toPoint:PrevioustouchLocationpoint];
CGAffineTransform newTransform = CGAffineTransformScale(arrow.transform, 1, 1);
CGFloat previousRotation = atan2(previousDifference.y, previousDifference.x);
CGPoint currentDifference = [self vectorFromPoint:origin toPoint:touchLocationpoint];
CGFloat currentRotation = atan2(currentDifference.y, currentDifference.x);
CGFloat newAngle = currentRotation- previousRotation;
temp1 = temp1 + newAngle;
//NSLog(#"temp1 is %f",temp1);
//NSLog(#"Angle:%F\n",(temp1*180)/M_PI);
newTransform = CGAffineTransformRotate(newTransform, newAngle);
[self animateView:arrow toPosition:newTransform];
}
-(void)animateView:(UIView *)theView toPosition:(CGAffineTransform) newTransform
{
arrow.transform = newTransform;
}
-(CGPoint)vectorFromPoint:(CGPoint)firstPoint toPoint:(CGPoint)secondPoint
{
CGFloat x = secondPoint.x - firstPoint.x;
CGFloat y = secondPoint.y - firstPoint.y;
CGPoint result = CGPointMake(x, y);
//NSLog(#"%f %f",x,y);
return result;
}
So,
i am trying to do a very simple disc rotation (2d), according to the user touch on it, just like a DJ or something.
It is working, but there is a problem, after certain amount of rotation, it starts going backwards, this amount is after 180 degrees or as i saw in while logging the angle, -3.14 (pi).
I was wondering, how can i achieve a infinite loop, i mean, the user can keep rotating and rotating to any side, just sliding his finger?
Also a second question is, is there any way to speed up the rotation?
Here is my code right now:
#import <UIKit/UIKit.h>
#interface Draggable : UIImageView {
CGPoint firstLoc;
UILabel * fred;
double angle;
}
#property (assign) CGPoint firstLoc;
#property (retain) UILabel * fred;
#end
#implementation Draggable
#synthesize fred, firstLoc;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
angle = 0;
if (self) {
// Initialization code
}
return self;
}
-(void)handleObject:(NSSet *)touches
withEvent:(UIEvent *)event
isLast:(BOOL)lst
{
UITouch *touch =[[[event allTouches] allObjects] lastObject];
CGPoint curLoc = [touch locationInView:self];
float fromAngle = atan2( firstLoc.y-self.center.y,
firstLoc.x-self.center.x );
float toAngle = atan2( curLoc.y-(self.center.y+10),
curLoc.x-(self.center.x+10));
float newAngle = angle + (toAngle - fromAngle);
NSLog(#"%f",newAngle);
CGAffineTransform cgaRotate = CGAffineTransformMakeRotation(newAngle);
self.transform = cgaRotate;
if (lst)
angle = newAngle;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch =[[[event allTouches] allObjects] lastObject];
firstLoc = [touch locationInView:self];
};
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleObject:touches withEvent:event isLast:NO];
};
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleObject:touches withEvent:event isLast:YES];
}
#end
And in the ViewController:
UIImage *tmpImage = [UIImage imageNamed:#"theDisc.png"];
CGRect cellRectangle;
cellRectangle = CGRectMake(-1,self.view.frame.size.height,tmpImage.size.width ,tmpImage.size.height );
dragger = [[Draggable alloc] initWithFrame:cellRectangle];
[dragger setImage:tmpImage];
[dragger setUserInteractionEnabled:YES];
dragger.layer.anchorPoint = CGPointMake(.5,.5);
[self.view addSubview:dragger];
I am open to new/cleaner/more correct ways of doing this too.
Thanks in advance.
Flip the angle if it's below -180 or above 180 degrees. Consider the following touchesMoved implementation:
#implementation RotateView
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA, CGPoint endLineA, CGPoint beginLineB, CGPoint endLineB)
{
CGFloat a = endLineA.x - beginLineA.x;
CGFloat b = endLineA.y - beginLineA.y;
CGFloat c = endLineB.x - beginLineB.x;
CGFloat d = endLineB.y - beginLineB.y;
CGFloat atanA = atan2(a, b);
CGFloat atanB = atan2(c, d);
// convert radians to degrees
return (atanA - atanB) * 180 / M_PI;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint curPoint = [[touches anyObject] locationInView:self];
CGPoint prevPoint = [[touches anyObject] previousLocationInView:self];
// calculate rotation angle between two points
CGFloat angle = angleBetweenLinesInDegrees(self.center, prevPoint, self.center, curPoint);
// Flip
if (angle > 180) {
angle -= 360;
} else if (angle < -180) {
angle += 360;
}
self.layer.transform = CATransform3DRotate(self.layer.transform, DEGREES_TO_RADIANS(angle), .0, .0, 1.0);
}
#end
When dragging around the outer bounds of the view, it will rotate it continuously like a spinning wheel. Hope it helps.
You have some problems here:
1-)
CGPoint curLoc = [touch locationInView:self];
and
firstLoc = [touch locationInView:self];
You are transforming your view, and then asking for the location of a touch in it. You cannot get the correct location of a touch in a rotated view.
Make them something not transformed. (for example self.superview after putting it in a container)
2-)
cellRectangle = CGRectMake(-1,self.view.frame.size.height,tmpImage.size.width ,tmpImage.size.height );
You are placing your Draggable instance out of the screen by passing self.view.frame.size.height as the CGRect's y parameter.