Calculate rotation angle for anchor point - ios

I got stuck with a problem where I need to reposition views to predefined locations.
All views have a UIPanGestureRecognizer and a UIRotationGestureRecognizer and are positioned/rotated inside the controllers view. Upon a certain event the views should move to a new position with a new rotation angle.
Everything works fine but a soon as one of the gesture recognizer was active and thus the anchorPoint has changed repositioning/rotation fails.
Here is my method I try to use to take the shift in the anchorPoint into account.
- (CGPoint)centerPointWithInVisibleAreaForPoint:(CGPoint)point
{
CGPoint anchorP = self.layer.anchorPoint;
anchorP.x -= 0.5;
anchorP.y -= 0.5;
CGRect rect = self.bounds;
CGFloat widthDelta = CGRectGetWidth(self.bounds) * anchorP.x;
CGFloat heightDelta = CGRectGetHeight(self.bounds) * anchorP.y;
CGPoint newCenter = CGPointMake(point.x + widthDelta, point.y + heightDelta);
return newCenter;
}
The controller asks for the corrected center point and sets the center value of the view. Afterwards the rotation transform is set using CGAffineTransformConcat(view.transform, CGAffineTransformMakeRotation(differenceAngle)).
I think the problem is caused by the fact that the predefined target angle is based on a rotation around the center which is obviously different when rotated around a different anchorPoint, but I don't know how to compensate for that.

The only solution I found (and it is after all the most easiest one) is to reset the anchorPoint to 0.5/0.5 and correct the position accordingly.
- (void)resetAnchorPoint
{
if (!CGPointEqualToPoint(self.layer.anchorPoint, CGPointMake(0.5, 0.5))) {
CGFloat width = CGRectGetWidth(self.bounds);
CGFloat height = CGRectGetHeight(self.bounds);
CGPoint newPoint = CGPointMake(width * 0.5, height * 0.5);
CGPoint oldPoint = CGPointMake(width * self.layer.anchorPoint.x, height * self.layer.anchorPoint.y);
newPoint = CGPointApplyAffineTransform(newPoint, self.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, self.transform);
CGPoint position = self.layer.position;
position.x += (newPoint.x - oldPoint.x);
position.y += (newPoint.y - oldPoint.y);
[CATransaction setDisableActions:YES];
self.layer.position = position;
self.layer.anchorPoint = CGPointMake(0.5, 0.5);
[CATransaction setDisableActions:NO];
}
}

Related

How To Get A Perfect Position Of Rotating View?

I am have one view and I am rotating that view using CABasicAnimation.
Now my problem is that how I get a perfect position of that view while rotating. I have tried many type of codes but i can't got a perfect position during rotation of that view.
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
NSNumber *currentAngle = [CircleView.layer.presentationLayer valueForKeyPath:#"transform.rotation"];
rotationAnimation.fromValue = currentAngle;
rotationAnimation.toValue = #(50*M_PI);
rotationAnimation.duration = 50.0f; // this might be too fast
rotationAnimation.repeatCount = HUGE_VALF; // HUGE_VALF is defined in math.h so import it
[CircleView.layer addAnimation:rotationAnimation forKey:#"rotationAnimationleft"];
I am using this code for rotating my view.
I have also attached a one photo of my view.
Thank you In Advance Please Help If You Know.
To get view's parameters during the animation you should use view.layer.presentationLayer
ADDED:
In order to get the coordinate of top left corner of the view, use the following code:
- (CGPoint)currentTopLeftPointOfTheView:(UIView *)view
{
CGRect rect = view.bounds;
rect.origin.x = view.center.x - 0.5 * CGRectGetWidth(rect);
rect.origin.y = view.center.y - 0.5 * CGRectGetHeight(rect);
CGPoint originalTopLeftCorner = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
CGPoint rectCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGFloat radius = sqrt(pow(rectCenter.x - originalTopLeftCorner.x, 2.0) + pow(rectCenter.y - originalTopLeftCorner.y, 2.0));
CGFloat originalAngle = M_PI - acos((rectCenter.x - originalTopLeftCorner.x) / radius);
CATransform3D currentTransform = ((CALayer *)view.layer.presentationLayer).transform;
CGFloat rotation = atan2(currentTransform.m12, currentTransform.m11);
CGFloat resultAngle = originalAngle - rotation;
CGPoint currentTopLeftCorner = CGPointMake(round(rectCenter.x + cos(resultAngle) * radius), round(rectCenter.y - sin(resultAngle) * radius));
return currentTopLeftCorner;
}
Resulting CGPoint will be the coordinate of the top left corner of your (rotated) view relative to its superview.
Set the position of the layer that you what
[layer setPosition:endpoint];
Or you may also refer this CABasicAnimation rotate returns to original position

CATransform3DMakeRotation Setting AnchorPoint and position. iOS7 View position mistake

CATransform3DMakeRotation Setting AnchorPoint and position
iOS7 View position mistake...
but iOS8 and iOS9 dont have this problem..
why?
-(void)viewDidAppear:(BOOL)animated
{
[self setAnchorPoint:CGPointMake(0.5, 1) forView:self.chieldImageView];
}
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
view.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
view.bounds.size.height * view.layer.anchorPoint.y);
newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);
CGPoint position = view.layer.position;
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}
This is probably due to the use of auto layout, on iOS8 and major they fixed it.
There are not so many solution, one is to keep the view you are transforming inside a container view. You use constraints on the container view to position it and in the contained (need to be transformed view) you just add it as a subview by keeping the -translatesAutoresizingMaskIntoConstraints to YES.
Check on Matt book.

SKSpriteNode ScaleUpFrom?

Problem
Scaling up a 2D image, scales up from the image centre point, however. I need it to scale up from a specific co-ordinate?
[backgroundImage setScale:rifleZoom];
My current technique to scaling up the image.
I need to scale up from centre screen as opposed to centre image
Now my way to place a rifle shot on the screen # centre is this way:
CGPoint positionNow = CGPointMake(backgroundImage.position.x, backgroundImage.position.y);
CGPoint positionPrev = CGPointMake(0.5, 0.5);
float xdiff = positionNow.x - positionPrev.x;
float ydiff = positionNow.y - positionPrev.y;
CGPoint newPositionOne = CGPointMake(0.5- xdiff, 0.5 - ydiff);
newPositionOne = CGPointMake(newPositionOne.x/rifleZoom, newPositionOne.y/rifleZoom);
Now this works perfectly no matter how much the image is scaled, however. I cannot seem to implement it into scaling the image up from the centre of the screen opposed to centre of the image.
What I've Tried
I've tried to change the position of the image before scaling it up. To the same same point make as newPositionOne However, this does not work or not being done right.
EDIT
This scales and brings centre to screen centrepoint, or messes up completely. It's a little too off the cuff to make a decision exactly what it's doing.
CGPoint positionNow = CGPointMake(backgroundImage.position.x, backgroundImage.position.y);
CGPoint positionPrev = CGPointMake(0.5, 0.5);
float xdiff = positionNow.x - positionPrev.x;
float ydiff = positionNow.y - positionPrev.y;
CGPoint newPositionOne = CGPointMake(0.5- xdiff, 0.5 - ydiff);
newPositionOne = CGPointMake(newPositionOne.x/rifleZoom, newPositionOne.y/rifleZoom);
double xPosition = newPositionOne.x / backgroundImage.size.width;
double yPosition = newPositionOne.y / backgroundImage.size.height;
CGPoint prevAnchorPoint = backgroundImage.anchorPoint;
backgroundImage.anchorPoint = CGPointMake(xPosition,yPosition);
double positionX = backgroundImage.position.x + (backgroundImage.anchorPoint.x - prevAnchorPoint.x) * backgroundImage.size.width;
double positionY = backgroundImage.position.y + (backgroundImage.anchorPoint.y - prevAnchorPoint.y) * backgroundImage.size.height;
backgroundImage.position = CGPointMake(positionX,positionY);
[backgroundImage runAction:[SKAction repeatAction:[SKAction scaleTo:rifleZoom duration:1.0] count:1]];
You can use the anchor point property of the background node to change the point from which image scales. By default the anchorPoint is at (0.5,0.5). This indicates the center of the node. If you make the anchorPoint (0,0), then its moved to the bottom left corner.
anchorPoint : Defines the point in the sprite that corresponds to the
node’s position. You specify the value for this property in the unit
coordinate space. The default value is (0.5,0.5), which means that the
sprite is centered on its position.
When you shift the anchorPoint, you have to adjust the background position again to counteract the movement due to changing the anchorPoint.
So you can use,
CGPoint xPosition = convertedPoint.x / background.size.width
CGPoint yPosition = convertedPoint.y / background.size.height
CGPoint prevAnchorPoint = background.anchorPoint
background.anchorPoint = CGPointMake(xPosition,yPosition)
CGFloat positionX = background.position.x + (background.anchorPoint.x - prevAnchorPoint.x) * background.size.width
CGFloat positionY = background.position.y + (background.anchorPoint.y - prevAnchorPoint.y) * background.size.height
background.position = CGPointMake(positionX,positionY)

iOS CGAffineTransformScale keep left anchor

I am using CGAffineTransformScale to scale down my UITableView, it is scaling it down correctly but i can't seem to set the anchor point correctly.
self.myTableView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.8, 0.8);
If I don't set the anchor point it scales it down from the center but as my requirement I need to keep the left top corner point fixed. For this I've used this
self.myTableView.layer.anchorPoint = CGPointMake(0, 0);
self.myTableView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.8, 0.8);
But this doesn't set the anchor correctly.. One more thing I haven't used AutoLayout
Please can anyone help me out on this…? thanks in advance..
Try this
[self setAnchorPoint:CGPointMake(0, 0.5) forView:recognizer.view]; // for left anchor point
and here setAnchorPoint
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
view.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
view.bounds.size.height * view.layer.anchorPoint.y);
newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);
CGPoint position = view.layer.position;
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}

How to rotate an layer at a specific anchor point without a skip?

I want to rotate a layer with an image at the top left corner, and not the center. According to the docs I set the anchorPoint property to [0, 1]. The view rotates in my example by 50°, but before it starts to animate, the view jumps to another point at the screen.
self.shakeTag.layer.anchorPoint = CGPointMake(0.0f, 1.0f);
[UIView beginAnimations:#"rotate" context:nil];
[self.shakeTag.layer setTransform:
CATransform3DRotate(CATransform3DIdentity,
radians(50.0), 0.0f, 0.0f, 1.0f)];
[UIView commitAnimations];
radians() is defined like this:
static inline double radians (double degrees) {return degrees * M_PI/180;}
When I use an image that is 4 times the size and has a lot of transparent pixels, I can rotate it at the default anchor point [0.5, 0.5], but I don’t want to waste the space for invisible pixels. Any ideas how I can prevent the layer from jumping before the rotation takes place?
Changing the anchor point affects the positioning of your view. You'll need to change the view's position if you change the anchor point and if you want to keep your view where it currently is. Use something like this (taken from here: Layer Position jumps at start of (Core) Animation) to set your anchor point and compensate position changes:
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y);
newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);
CGPoint position = view.layer.position;
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}
Also see here for more details: Changing my CALayer's anchorPoint moves the view

Resources