UIButton - move and scale - ios

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.

Related

Define custom touch area in custom UIControl object

I am creating a custom UIControl object as detailed here. It is all working well except for the touch area.
I want to find a way to limit the touch area to only part of the control, in the example above I want it to be restricted to the black circumference only rather than the whole control area.
Any idea?
Cheers
You can override UIView's pointInside:withEvent: to reject unwanted touches.
Here's a method that checks if the touch occurred in a ring around the center of the view:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
UITouch *touch = [[event touchesForView:self] anyObject];
if (touch == nil)
return NO;
CGPoint touchPoint = [touch locationInView:self];
CGRect bounds = self.bounds;
CGPoint center = { CGRectGetMidX(bounds), CGRectGetMidY(bounds) };
CGVector delta = { touchPoint.x - center.x, touchPoint.y - center.y };
CGFloat squareDistance = delta.dx * delta.dx + delta.dy * delta.dy;
CGFloat outerRadius = bounds.size.width * 0.5;
if (squareDistance > outerRadius * outerRadius)
return NO;
CGFloat innerRadius = outerRadius * 0.5;
if (squareDistance < innerRadius * innerRadius)
return NO;
return YES;
}
To detect other hits on more complex shapes you can use a CGPath to describe the shape and test using CGPathContainsPoint. Another way is to use an image of the control and test the pixel's alpha value.
All that depends on how you build your control.

Rotate imageView as per touch

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;
}

UIImage which follow touches

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

UIImageView rotation animation in the touched direction

I have one UIImageView having an image of an arrow. When user taps on the UIView this arrow should point to the direction of the tap maintaing its position it should just change the transform. I have implemented following code. But it not working as expected. I have added a screenshot. In this screenshot when i touch the point upper left the arrow direction should be as shown.But it is not happening so.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[[event allTouches]anyObject];
touchedPoint= [touch locationInView:touch.view];
imageViews.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(rangle11));
previousTouchedPoint = touchedPoint ;
}
- (CGFloat) pointPairToBearingDegrees:(CGPoint)startingPoint secondPoint:(CGPoint) endingPoint
{
CGPoint originPoint = CGPointMake(endingPoint.x - startingPoint.x, endingPoint.y - startingPoint.y); // get origin point to origin by subtracting end from start
float bearingRadians = atan2f(originPoint.y, originPoint.x); // get bearing in radians
float bearingDegrees = bearingRadians * (180.0 / M_PI); // convert to degrees
bearingDegrees = (bearingDegrees > 0.0 ? bearingDegrees : (360.0 + bearingDegrees)); // correct discontinuity
return bearingDegrees;
}
I assume you wanted an arrow image to point to where ever you touch, I tried and this is what i could come up with. I put an image view with an arrow pointing upwards (haven't tried starting from any other position, log gives correct angles) and on touching on different locations it rotates and points to touched location. Hope it helps ( tried some old math :-) )
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[[event allTouches]anyObject];
touchedPoint= [touch locationInView:touch.view];
CGFloat angle = [self getAngle:touchedPoint];
imageView.transform = CGAffineTransformMakeRotation(angle);
}
-(CGFloat) getAngle: (CGPoint) touchedPoints
{
CGFloat x1 = imageView.center.x;
CGFloat y1 = imageView.center.y;
CGFloat x2 = touchedPoints.x;
CGFloat y2 = touchedPoints.y;
CGFloat x3 = x1;
CGFloat y3 = y2;
CGFloat oppSide = sqrtf(((x2-x3)*(x2-x3)) + ((y2-y3)*(y2-y3)));
CGFloat adjSide = sqrtf(((x1-x3)*(x1-x3)) + ((y1-y3)*(y1-y3)));
CGFloat angle = atanf(oppSide/adjSide);
// Quadrant Identifiaction
if(x2 < imageView.center.x)
{
angle = 0-angle;
}
if(y2 > imageView.center.y)
{
angle = M_PI/2 + (M_PI/2 -angle);
}
NSLog(#"Angle is %2f",angle*180/M_PI);
return angle;
}
-anoop4real
Given what you told me, I think the problem is that you are not resetting your transform in touchesBegan. Try changing it to something like this and see if it works better:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[[event allTouches]anyObject];
touchedPoint= [touch locationInView:touch.view];
imageViews.transform = CGAffineTransformIdentity;
imageViews.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(rangle11));
previousTouchedPoint = touchedPoint ;
}
Do you need the line to "remove the discontinuity"? Seems atan2f() returns values between +π to -π. Won't those work directly with CATransform3DMakeRotation()?
What you need is that the arrow points to the last tapped point. To simplify and test, I have used a tap gesture (but it's similar to a touchBegan:withEvent:).
In the viewDidLoad method, I register the gesture :
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[self.view addGestureRecognizer:tapGesture];
[tapGesture release];
The method called on each tap :
- (void)tapped:(UITapGestureRecognizer *)gesture
{
CGPoint imageCenter = mFlecheImageView.center;
CGPoint tapPoint = [gesture locationInView:self.view];
double deltaY = tapPoint.y - imageCenter.y;
double deltaX = tapPoint.x - imageCenter.x;
double angleInRadians = atan2(deltaY, deltaX) + M_PI_2;
mFlecheImageView.transform = CGAffineTransformMakeRotation(angleInRadians);
}
One key is the + M_PI_2 because UIKit coordinates have the origin at the top left corner (while in trigonometric, we use a bottom left corner).

touchesMoved:withEvent: and UIRotationgestureRecognizer don't work together

I'm trying these two methods to move and rotate an UIView. Both methods work separately but if I rotate and then move the UIView it disappears.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGRect rect = self.aView.frame;
UITouch *touch = [touches anyObject];
CGPoint pPoint = [touch previousLocationInView:self.view];
CGPoint cPoint = [touch locationInView:self.view];
float deltaX = cPoint.x - pPoint.x;
float deltaY = cPoint.y - pPoint.y;
rect.origin.x = rect.origin.x + deltaX;
rect.origin.y = rect.origin.y + deltaY;
self.aView.frame = rect;
}
- (void)rotate:(UIRotationGestureRecognizer *) recognizer {
CGFloat rotation = angle + recognizer.rotation;
NSLog(#"%f", angle * 180 / M_PI);
self.aView.transform = CGAffineTransformMakeRotation (rotation);
if (recognizer.state == UIGestureRecognizerStateEnded)
angle = rotation;
}
Gesture recognisers take priority over touchMoved, so it's hard to use them both with the same view.
Use a UIPanGestureRecognizer instead of touchMoved to handle dragging the UIView. You can then get the UIPanGestureRecognizer and UIRotationGestureRecognizer to cooperate with one another by implementing the
– gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
method, which is defined in the UIGestureRecognizerDelegate protocol.

Resources