Hi I have a UIImageView which is part on, part off screen.
I want to rotate the view around a point off screen (the center of the view.)
How do I do this?
fullRotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotationAnimation.fromValue= [NSNumber numberWithFloat:0.0f];
fullRotationAnimation.toValue = [NSNumber numberWithFloat:2 * M_PI];
fullRotationAnimation.duration = 4;
fullRotationAnimation.repeatCount = 1;
[self->greyRings.layer addAnimation:fullRotationAnimation forKey:#"360"];
I'm using that code at the moment but it just spins it around the center of the screen.
Any ideas please?
Using block animation:
- (void)rotateImageView:(UIImageView *)imageView aroundPoint:(CGPoint)point byAngle:(CGFloat)angle {
CGFloat sinA = sin(angle);
CGFloat cosA = cos(angle);
CGFloat x = point.x;
CGFloat y = point.y;
CGAffineTransform transform = CGAffineTransformMake(cosA,sinA,-sinA,cosA,x-x*cosA+y*sinA,y-x*sinA-y*cosA);
[UIView animateWithDuration:4.0 animations:^{
imageView.transform = transform;
}];
}
Note that angle is in radians, so a full rotation is 2.0*M_PI.
Maybe you have to set anchorPoint of CALayer: see Specifying a Layer’s Geometry for more detail.
//(0,0) is the left-bottom of your layer bounds
self->greyRings.layer.anchorPoint = CGPointMake(0.0f, 0.0f);
I think a negative anchorPoint should also work. So you'd better give your bounds and we can calculate for you.
Related
I know how to rotate a view from right to left
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
transform.m34 = 0.0015;
view.layer.transform = transform;
But i cant rotate it from left to right.
Please help me.
I'm not sure what you mean by “left to right” and “right to left”. Anyway, as you have discovered, you can't control the direction of the animation just by setting the transform matrix, because both -M_PI and +M_PI have the same final effect, so there's no way for Core Animation to reliably know which way you want it to rotate.
Instead, you can animate the transform.rotation.y key path of the layer. This is a little more complicated to do, particularly since you want to animate a view's layer instead of a freestanding layer (not controlled by a view).
Anyway, here's the code I tested:
- (IBAction)rotateButtonWasTapped:(id)sender {
// Establish the perspective to be used during the animation.
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 0.0015;
self.imageView.layer.transform = transform;
static CGFloat kAngle = -M_PI;
[CATransaction begin]; {
// After the animation completes, make the transform “permanent”. Otherwise it
// will be undone when the animation is removed from the layer.
[CATransaction setCompletionBlock:^{
self.imageView.layer.transform = CATransform3DRotate(transform, kAngle, 0, 1, 0);
}];
// Animate the rotation using Core Animation's extension to Key Value Coding.
// The sign of kAngle determines the direction of rotation.
CABasicAnimation *animation = [CABasicAnimation
animationWithKeyPath:#"transform.rotation.y"];
animation.fromValue = #0;
animation.toValue = #(kAngle);
animation.duration = 1;
[self.imageView.layer addAnimation:animation forKey:animation.keyPath];
} [CATransaction commit];
}
This rotates the view such that the left edge of the view moves “closer” to the user during the rotation. If that's not what you mean by “left to right”, change the definition of kAngle to M_PI and it will rotate in the other direction. I have tested both ways. You can even spin the layer multiple times in one animation. For example, try defining kAngle as -3 * M_PI.
I did not find anything in the function documentation, but I think you can just make the rotation angle negative to rotate into the other direction.
if you want to rotate the view, back to the initial position... just do it with:
transform = CATransform3DMakeRotation(0, 0.0f, 1.0f, 0.0f)
or you can "combine" more..
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DMakeRotation(-M_PI_2, 0.0f, 1.0f, 0.0f);
transform = CATransform3DMakeRotation(0, 0.0f, 1.0f, 0.0f);
transform.m34 = 0.0015;
view.layer.transform = transform;
You can try... to use CATransform3D transform = starLayer.transform; instead of CATransform3D transform = CATransform3DIdentity;
-(void)updateTimer
{
if(update<125)
update=update+1;
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval interval = now - start;
int inter = (int)(interval*update);
inter*=10;
milliSec = (inter) % 1000;
seconds = (inter/1000) % 60;
minutes = (inter/60000) % 60;
timeValue = oldTimeValue+inter;
// Set Digital clock
// Set Analog clock hands
secHand.layer.transform = CATransform3DMakeRotation (Degrees2Radians(-(timeValue%1000)*360/1000), 0, 0, 1);
}
Here's the basic code to get started with:
[UIView transitionWithView:self.view duration:2.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^ {
CALayer *layer1 = leftband.layer;
layer1.anchorPoint = CGPointMake(0, 0.5);
CATransform3D rotationAndPerspectiveTransform1 = CATransform3DIdentity;
rotationAndPerspectiveTransform1.m34 = 1.0 / -500;
rotationAndPerspectiveTransform1 = CATransform3DRotate(rotationAndPerspectiveTransform1, DegreesToRadians(-180.0f), 0.0f, 1.0f, 0.0f);
layer1.transform = rotationAndPerspectiveTransform1;
CALayer *layer2 = rightband.layer;
layer2.anchorPoint = CGPointMake(0, 0.5);
CATransform3D rotationAndPerspectiveTransform2 = CATransform3DIdentity;
rotationAndPerspectiveTransform2.m34 = 1.0 / -500;
rotationAndPerspectiveTransform2 = CATransform3DRotate(rotationAndPerspectiveTransform2, DegreesToRadians(180.0f), 0.0f, 1.0f, 0.0f);
layer2.transform = rotationAndPerspectiveTransform2;
}
completion:nil {
}];
leftband and rightband are two UIImageViews. The effect I want is that the leftband animates with its transform along the Y-axis towards the left direction. And the rightband does the same along its Y-axis towards the right.
But the above code results in both left and right imageviews animating in the same direction (it happens only towards left). I guess my understanding of anchor points is wrong. Both -180° and +180° in the rotation transform result in a leftward animation.
What am I doing wrong here?
Basically setting the anchor points before the animation block runs solves the issue. Anchor point for left -> (0.0f,0.5f) and for right -> (1.0f,0.5f).
I've managed to get along using [UIView animateWithDuration... to get animations done that I need in my UI. Now I want to move an image along a curved path, and that whole CAAnimation cluster looks pretty daunting to me.
I'd be much obliged if someone could help me fill in the method I wish I could code, which would look like this:
- (void)makeAnImageFlyFrom:(UIImageView *)imageViewA to:(UIImageView *)imageViewB alongPath:(CGMutablePathRef)path duration:(NSTimeInterval)duration {
UIImage *imageToFly = imageViewA.image;
// magic, that i'm too lazy to learn right now goes here
// image flys along the path and gets scaled to match imageViewB.
// then view/layer hierarchy is just as it was, but imageViewB has a new image
// maybe this happens on animationDidStop...
imageViewB.image = imageToFly;
}
Feel free to replace params (like path ref) if you think there's a smarter interface for this kind of method. Thanks in advance.
Ok, a whole Sunday later, here's a method that I think works. I'm still unclear about a few things, noted in comments, but if you need this type of thing, feel free to cut and paste. I promise not call you lazy:
- (void)makeAnImageFlyFrom:(UIImageView *)imageViewA to:(UIImageView *)imageViewB duration:(NSTimeInterval)duration {
// it's simpler but less general to not pass in the path. i chose simpler because
// there's a lot of geometry work using the imageView frames here anyway.
UIImageView *animationView = [[UIImageView alloc] initWithImage:imageViewA.image];
animationView.tag = kANIMATION_IMAGE_TAG;
animationView.frame = imageViewA.frame;
[self addSubview:animationView];
// scale
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
[resizeAnimation setFromValue:[NSValue valueWithCGSize:imageViewA.bounds.size]];
[resizeAnimation setToValue:[NSValue valueWithCGSize:imageViewB.bounds.size]];
// build the path
CGRect aRect = [imageViewA convertRect:imageViewA.bounds toView:self];
CGRect bRect = [imageViewB convertRect:imageViewB.bounds toView:self];
// unclear why i'm doing this, but the rects converted to this view's
// coordinate system seemed have origin's offset negatively by half their size
CGFloat startX = aRect.origin.x + aRect.size.width / 2.0;
CGFloat startY = aRect.origin.y + aRect.size.height / 2.0;
CGFloat endX = bRect.origin.x + bRect.size.width / 2.0;
CGFloat endY = bRect.origin.y + bRect.size.height / 2.0;
CGFloat deltaX = endX - startX;
CGFloat deltaY = endY - startY;
// these control points suited the path i needed. your results may vary
CGFloat cp0X = startX + 0.3*deltaX;
CGFloat cp0Y = startY - 1.3*deltaY;
CGFloat cp1X = endX + 0.1*deltaX;
CGFloat cp1Y = endY - 0.5*deltaY;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, startX, startY);
CGPathAddCurveToPoint(path, NULL, cp0X, cp0Y, cp1X, cp1Y, endX, endY);
// keyframe animation
CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
keyframeAnimation.calculationMode = kCAAnimationPaced;
keyframeAnimation.fillMode = kCAFillModeForwards;
keyframeAnimation.removedOnCompletion = NO;
keyframeAnimation.path = path;
// assuming i need to manually release, despite ARC, but not sure
CGPathRelease(path);
// a little unclear about the fillMode, but it works
// also unclear about removeOnCompletion, because I remove the animationView
// but that seems to be insufficient
CAAnimationGroup *group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[group setAnimations:[NSArray arrayWithObjects:keyframeAnimation, resizeAnimation, nil]];
group.duration = duration;
group.delegate = self;
// unclear about what i'm naming with the keys here, and why
[group setValue:animationView forKey:#"animationView"];
[animationView.layer addAnimation:group forKey:#"animationGroup"];
}
// clean up after like this
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
UIImageView *imageViewForAnimation = (UIImageView *)[self viewWithTag:kANIMATION_IMAGE_TAG];
// get the imageView passed to the animation as the destination
UIImageView *imageViewB = (UIImageView *)[self viewWithTag:kDEST_TAG];
imageViewB.image = imageViewForAnimation.image;
[imageViewForAnimation removeFromSuperview];
}
Dear All,
I want to rotate a UIImageView relative to a point in touchesMoved method.
I tried 2 -3 methods but I am not getting the exact result what I expected to be.
First method I used
CGAffineTransform transforms = CGAffineTransformMakeRotation(M_PI/2);
imgView.transform = transforms;
This is written in touchesMoved. So I expect a rotation for the image view in each touchesMoved.
But the rotation is occuring only once .
Second method I used was
CGAffineTransform transforms = CGAffineTransformRotate(imgView.transform, M_PI/2);
imgView.transform = transforms;
Now the result what I get is the image in Image view is continusely rotating in each move. But imageview is not rotating. What i need is to rotate the imageview not the image.
Any help will be greatly appreciated.
Thanks & Best Regards,
Rupesh R Menon
If I understood correctly you want to achieve single finger rotation on the image. If it is so you can use following functions from one of my live working project. You can use this for single image as well as multiple images. You need to modify at some extend for multiple images. Best way is extend UIImageView class and create your own class.
From touches moved call this function
[self transformImagewithTouches:touch];
Declare 1 property and synthesize as follows. (Also please declare other variables if required in below code.
#property (nonatomic) CGFloat fltRotatedAngle;
-(void)transformImagewithTouches:(UITouch *)touchLocation
{
//NSLog(#"Before %f",self.fltRotatedAngle);
CGPoint touchLocationpoint = [touchLocation locationInView:[self superview]];
CGPoint PrevioustouchLocationpoint = [touchLocation previousLocationInView:[self superview]];
CGPoint origin;
origin.x=self.center.x;
origin.y=self.center.y;
CGPoint previousDifference = [self vectorFromPoint:origin toPoint:PrevioustouchLocationpoint];
CGAffineTransform newTransform =CGAffineTransformScale(self.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;
//Calculate Angle to Store
fltTmpAngle = fltTmpAngle+newAngle;
self.fltRotatedAngle = (fltTmpAngle*180)/M_PI;
newTransform = CGAffineTransformRotate(newTransform, newAngle);
[self animateImageView:self toPosition:newTransform];
}
-(void)animateImageView:(UIImageView *)theView toPosition:(CGAffineTransform) newTransform
{
[UIView setAnimationsEnabled:YES];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.0750];
self.transform = newTransform;
[UIView commitAnimations];
}
-(CGPoint)vectorFromPoint:(CGPoint)firstPoint toPoint:(CGPoint)secondPoint
{
CGFloat x = secondPoint.x - firstPoint.x;
CGFloat y = secondPoint.y - firstPoint.y;
CGPoint result = CGPointMake(x, y);
return result;
}
Hope it helps. If you stuck up please let me know I ll definitely help you.
Thanks for the interest shown by you in helping me.
When I used the following code I was able to rotate the image view in my desired angle.
imgView.layer.transform = CATransform3DMakeRotation(pCalculator.tangentToCornerAngle, 0, 0, 1);
where imgView is an UIImageView.
pCalculator.tangentToCornerAngle is the desired angle in which rotation has to be made.
Function is CATransform3DMakeRotation(CGFloat angle, CGFloat x, <CGFloat y, CGFloat z);
Thankyou All,
Rupesh R Menon
It's because you have rotated it by M_PI/2 and when you rotate it again it rotates it from the initial position. Use this one
CGAffineTransform transforms = CGAffineTransformConcat(imgView.transform,CGAffineTransformMakeRotation(M_PI/2));
imgView.transform = transforms;
I want to add a back image to a CALayer when the my rotation transform degree is higher than 90.
It is just like a Flipboard flip animation.
This is my current code:
CALayer *layer = self.view.layer;
int frames = 130;
layer.anchorPoint = CGPointMake(0, 0.5);
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / -2000.0;
transform = CATransform3DRotate(transform, -frames * M_PI / 180.0, 0.0, 1.0, 0.0);
self.view.layer.transform = transform;
CAKeyframeAnimation *pivot = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
NSMutableArray *values = [[NSMutableArray alloc] initWithCapacity:45];
NSMutableArray *times = [[NSMutableArray alloc] initWithCapacity:45];
for (int i = 0; i < frames; i++) {
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / -2000.0;
transform = CATransform3DRotate(transform, -M_PI / 180.0 * i, 0.0, 1.0, 0.0);
[values addObject:[NSValue valueWithCATransform3D:transform]];
[times addObject:[NSNumber numberWithFloat:(float)i / (frames - 1)]];
}
pivot.values = values;
pivot.keyTimes = times;
[values release];
[times release];
pivot.duration = 0.5f;
pivot.calculationMode = kCAAnimationLinear;
[layer addAnimation: pivot forKey: #"pivot"];
Could anyone tell me how to add a back image just like the flip effect.
As far as I know, the easiest way to do it is to add two sublayers representing the front side and the back side of the page. Set both the sublayers' doubleSided property to NO. Set the back side layer transform to the same flipped transform you're animating to. Then, when the page's front side faces the viewer, the front side layer is visible and the back side layer is "hidden", and vice versa.
See GeekGameBoard source code (Card.m) for a sample of the technique.