How To Get A Perfect Position Of Rotating View? - ios

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

Related

iOS Wave Effect Animated Bezier Curve

I am trying to achieve an animation effect which will simulate a very simple representation of a wave effect by using bezierpath and keyframeanimation.
However, with key frame animation I cant get the effect properly, the animation is working from start point to end instead of making wave curves up and down.
This could be a better representation of what is happening.
Bezier Curve Wave Animation
and this is the code for the animation:
CGPoint rightCurve;
rightCurve.x = center.x + radius * cos(startAngle);
rightCurve.y = center.y + radius * sin(startAngle);
CGPoint leftCurve;
leftCurve.x = center.x + radius * cos(endAngle);
leftCurve.y = center.y + radius * sin(endAngle);
CGPoint centerCurve;
centerCurve.x = center.x;
centerCurve.y = leftCurve.y;
CGPoint controlLeftUp = CGPointMake((leftCurve.x + centerCurve.x) /2, ((centerCurve.y + ((centerCurve.x - rightCurve.x)/2)*1.3)));
CGPoint controlLeftDown = CGPointMake((leftCurve.x + centerCurve.x) /2, ((centerCurve.y - ((centerCurve.x - rightCurve.x)/2)*1.3)));
CGPoint controlRightDown = CGPointMake((rightCurve.x + centerCurve.x) /2, ((centerCurve.y - ((centerCurve.x - rightCurve.x)/2)*1.3)));
CGPoint controlRightUp = CGPointMake((rightCurve.x + centerCurve.x) /2, ((centerCurve.y + ((centerCurve.x - rightCurve.x)/2)*1.3)));
UIBezierPath* path0 = [UIBezierPath bezierPath];
[path0 moveToPoint:CGPointMake(leftCurve.x, leftCurve.y)];
[path0 addQuadCurveToPoint:CGPointMake(centerCurve.x, centerCurve.y) controlPoint:controlLeftUp];
[path0 moveToPoint:CGPointMake(centerCurve.x , centerCurve.y)];
[path0 addQuadCurveToPoint:CGPointMake(rightCurve.x, rightCurve.y) controlPoint:controlRightDown];
UIBezierPath* path1 = [UIBezierPath bezierPath];
[path1 moveToPoint:CGPointMake(rightCurve.x,rightCurve.y)];
[path1 addQuadCurveToPoint:CGPointMake(centerCurve.x, centerCurve.y) controlPoint:controlRightUp];
[path1 moveToPoint:CGPointMake(centerCurve.x , centerCurve.y)];
[path1 addQuadCurveToPoint:CGPointMake(leftCurve.x, leftCurve.y) controlPoint:controlLeftDown];
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:#"path"];
animation.duration = _animationInterval;
animation.values = [NSArray arrayWithObjects:(id)path0.CGPath, (id)path1.CGPath, nil];
animation.repeatCount = HUGE_VALF;
[shapeLayer addAnimation:animation forKey:nil];
It will be amazing if someone tell me how to edit the animation or the curve.
Thanks in advance.

iOS: How to animate an image along a sine curve?

As in the title stated, I want to to animate some bubbles that travel along a sine curve from the bottom to the top within an UIView using CG/CA or even OpenGL if necessary.
Here is my CA code snippet that works fine, but it's a straight line which is animated. How can I build in the sine curve behaviour ?
- (void)animateBubble:(UIImageView *)imgView {
[UIView beginAnimations:[NSString stringWithFormat:#"%i",imgView.tag] context:nil];
[UIView setAnimationDuration:6];
[UIView setAnimationDelegate:self];
imgView.frame = CGRectMake(imgView.frame.origin.x, 0, imgView.frame.size.width, imgView.frame.size.height);
[UIView commitAnimations];
}
I've already achieved the wanted result with SpriteKit (have a look: http://youtu.be/Gnj3UAD3gQI). The bubbles travel along a sine curve from bottom to the top where the amplitude is within a random range. Moreover, I use the gyroscope sensors to additionally influence the path.
Is this behaviour somehow reproducible with UIKit, CG, CA (if absolutely necessary also with OpenGL) ? Code samples would be wonderful, but any other ideas are also highly appreciated.
To animate along a path, you first need to define that path. If you really need a true sine curve, we can show you how to do that, but it's probably easiest to define something that approximates a sine curve using two quadratic bezier curves:
CGFloat width = ...
CGFloat height = ...
CGPoint startPoint = ...
CGPoint point = startPoint;
CGPoint controlPoint = CGPointMake(point.x + width / 4.0, point.y - height / 4.0);
CGPoint nextPoint = CGPointMake(point.x + width / 2.0, point.y);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:point];
[path addQuadCurveToPoint:nextPoint controlPoint:controlPoint];
point = nextPoint;
controlPoint = CGPointMake(point.x + width / 4.0, point.y + height / 4.0);
nextPoint = CGPointMake(point.x + width / 2.0, point.y);
[path addQuadCurveToPoint:nextPoint controlPoint:controlPoint];
That renders path like so:
Obviously, change startPoint, width, and height to be whatever you want. Or repeat that process of adding more bezier paths if you need more iterations.
Anyway, having defined a path, rather than rendering the path itself, you can create a CAKeyframeAnimation that animates the position of the UIView along that path:
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.duration = 1.0;
animation.path = path.CGPath;
[view.layer addAnimation:animation forKey:#"myPathAnimation"];

Core Animation rotation around point

I would like to make an IBOutlet rotate around a specific point in the parent view,
currently i only know how to rotate it around an anchor point, however, i want to use a point outside the object's layer.
The rotation angle is calculated relative to the device heading from the point.
- (void)viewDidLoad{
[super viewDidLoad];
locationManager=[[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.headingFilter = 1;
locationManager.delegate=self;
[locationManager startUpdatingHeading];
[locationManager startUpdatingLocation];
compassImage.layer.anchorPoint=CGPointZero;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{
// Convert Degree to Radian and move the needle
float oldRad = -manager.heading.trueHeading * M_PI / 180.0f;
float newRad = -newHeading.trueHeading * M_PI / 180.0f;
CABasicAnimation *theAnimation;
theAnimation=[CABasicAnimation animationWithKeyPath:#"transform.rotation"];
theAnimation.fromValue = [NSNumber numberWithFloat:oldRad];
theAnimation.toValue=[NSNumber numberWithFloat:newRad];
theAnimation.duration = 0.5f;
[compassImage.layer addAnimation:theAnimation forKey:#"animateMyRotation"];
compassImage.transform = CGAffineTransformMakeRotation(newRad);
NSLog(#"%f (%f) => %f (%f)", manager.heading.trueHeading, oldRad, newHeading.trueHeading, newRad);
}
How can i rotate the UIImageView around (x,y) by alpha?
For rotating around a specific point (internal or external) you can change the anchor point of the layer and then apply a regular rotation transform animation similar to what I wrote about in this blog post.
You just need to be aware that the anchor point also affects where the layer appears on the screen. When you change the anchor point you will also have to change the position to make the layer appear in the same place on the screen.
Assuming that the layer is already placed in it's start position and that the point to rotate around is known, the anchor point and the position can be calculated like this (note that the anchor point is in the unit coordinate space of the layer's bounds (meaning that x and y range from 0 to 1 within the bounds)):
CGPoint rotationPoint = // The point we are rotating around
CGFloat minX = CGRectGetMinX(view.frame);
CGFloat minY = CGRectGetMinY(view.frame);
CGFloat width = CGRectGetWidth(view.frame);
CGFloat height = CGRectGetHeight(view.frame);
CGPoint anchorPoint = CGPointMake((rotationPoint.x-minX)/width,
(rotationPoint.y-minY)/height);
view.layer.anchorPoint = anchorPoint;
view.layer.position = rotationPoint;
Then you simply apply a rotation animation to it, for example:
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotate.toValue = #(-M_PI_2); // The angle we are rotating to
rotate.duration = 1.0;
[view.layer addAnimation:rotate forKey:#"myRotationAnimation"];
Just note that you have changed the anchor point so the position is no longer in the center of the frame and if you apply other transforms (such as a scale) they will also be relative to the anchor point.
Translated David's answer to swift 3:
let rotationPoint = CGPoint(x: layer.frame.width / 2.0, y: layer.frame.height / 2.0) // The point we are rotating around
print(rotationPoint.debugDescription)
let width = layer.frame.width
let height = layer.frame.height
let minX = layer.frame.minX
let minY = layer.frame.minY
let anchorPoint = CGPoint(x: (rotationPoint.x-minX)/width,
y: (rotationPoint.y-minY)/height)
layer.anchorPoint = anchorPoint;
layer.position = rotationPoint;

Label curve segments

I want to label every part of the following curve like in the figure. The label origin should start from the middle of the curve segment see the graphic. I think that something is not quite right in my logic. Could you assist where is the error in my code? I think that the code and the figure explain itself.
Thanks for your time and help!
CGFloat radius = self.height / 3.2f; // radius of the curve
CGPoint center = CGPointMake(self.width / 2.f, self.height / 2.f); //center of the curve
UIBezierPath* circlePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:degreesToRadian(270.f) endAngle:DEGREES_TO_RADIANS(269.999f) clockwise:YES]; // go around like the clock
CGFloat strokeStart = 0.f;
// Add the parts of the curve to the view
for (int i = 0; i < segmentValues.count; i++) {
NSNumber *value = [segmentValues objectAtIndex:i];
CGFloat strokeEnd = [value floatValue] / kPercentHundred;
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.lineCap = kCALineCapButt;
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [[colors objectAtIndex:i] CGColor];
circle.lineWidth = kWidhtLine;
circle.zPosition = 1.f;
circle.strokeStart = strokeStart;
circle.strokeEnd = strokeStart + strokeEnd;
[self.layer addSublayer:circle];
UIView *example = [[UIView alloc] init];
example.backgroundColor = kColorRed;
example.size = CGSizeMake(10, 10);
// middle = "(circle.strokeStart + circle.strokeEnd) / 2.f"
// * convert percent to degree
CGFloat angle = (circle.strokeStart + circle.strokeEnd) / 2.f * 360.f;
// determine the point and add the right offset
example.origin = CGPointMake(cosf(angle) * radius + center.x, sinf(angle) * radius + center.y);
[self addSubview:example];
strokeStart = circle.strokeEnd;
}
What I get see the figure.
What I expect in concept is something similar to the following figure.
There are two key issues:
You calculate the angle via:
CGFloat angle = (circle.strokeStart + circle.strokeEnd) / 2.f * 360.f;
You then proceed to use that for the cos and sin functions. Those expect radian values, though, so you have to convert this to radians before you use it in those functions. Thus, yielding:
CGFloat angle = degreesToRadian((circle.strokeStart + circle.strokeEnd) / 2.f * 360.f);
(BTW, I don't know if you prefer degreesToRadian or DEGREES_TO_RADIANS, but you should probably use one or the other.)
Or, more simply, you can convert to radians directly:
CGFloat angle = (circle.strokeStart + circle.strokeEnd) / 2.f * M_PI * 2.0;
You have rotated the circle 90 degrees (presumably so that it would start a "12 o'clock" rather than "3 o'clock"). Well, if you rotate the circle, then you have to rotate angle when you calculate where to put your example view, as well:
example.center = CGPointMake(cosf(angle - M_PI_2) * radius + center.x, sinf(angle - M_PI_2) * radius + center.y);
This yields:
I'm not sure what your goal is. You say you want to create labels, but what you create in your code is generic UIView objects.
Then you are setting a property "origin" on the view, but UIView does not have an origin as far as I know. It has a frame, which has an origin component, but not an origin property. I'm guessing that the code you posted is not actual, running code.
Do you want the labels to be centered on top of the circle? If so, you should both set the text alignment to NSTextAlignmentCenter, and set the center property of the views rather than the frame.origin.
I have a working sample project on github that creates an analog clock and puts labels for the hours around it. You can look at the code for an idea of how to do it:
Analog clock sample project
(The project also shows how to use the new spring physics UIView animation method.)
hmm it i a little late for me ;)
I think you have to use UIView::setCenter this will solve you problem. Then the center of the view is on your circle.
Edit: And it is easier to manipulate the position.

Calculate rotation angle for anchor point

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

Resources