I am using this code to zoom from a particular point
CGPoint getCenterPointForRect(CGRect inRect)
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
return CGPointMake((screenRect.size.height-inRect.origin.x)/2,(screenRect.size.width-inRect.origin.y)/2);
}
-(void) startAnimation
{
CGPoint centerPoint = getCenterPointForRect(self.view.frame);
self.view.transform = CGAffineTransformMakeTranslation(centerPoint.x, centerPoint.y);
self.view.transform = CGAffineTransformScale( self.view.transform , 0.001, 0.001);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:kTransitionDuration];
self.view.transform = CGAffineTransformIdentity;
[UIView commitAnimations];
}
Its not working. What is the correct way to do zooming from a particular point.
I think that, if I've diagnosed your problem correctly, you're getting a scaling animation where the view starts tiny and at a point, then scales and moves to the centre of the screen exactly like you want, but the point it starts at is incorrect?
First of all, views scale around their centre. So if you took out the translation and hence reduced the code you have to:
self.view.transform = CGAffineTransformMakeScale( 0.001, 0.001);
And your view ends up taking up the whole screen then it'll remain centred on the middle of the screen, sort of a little as though it's a long way away and you're heading directly towards it.
Supposing you instead want it to grow and move to the centre of the screen from (x, y) then you need something more like:
CGPoint locationToZoomFrom = ... populated by you somehow ...;
CGPoint vectorFromCentreToPoint = CGPointMake(
locationToZoomFrom.x - self.view.center.x,
locationToZoomFrom.y - self.view.center.y);
self.view.transform = CGAffineTransformMakeTranslation(vectorFromCentreToPoint.x, vectorFromCentreToPoint.y);
self.view.transform = CGAffineTransformScale( self.view.transform , 0.001, 0.001);
Where locationToZoomFrom will be the initial centre of the view and its normal centre as per its frame will be the destination.
Related
I have done animation opening a page on tapping on it but now I have to change it for Pan Gesture. How should I start?
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
CALayer *layer = ges.view.layer;
CATransform3D initialTransform = ges.view.layer.transform;
initialTransform.m34 = 1.0 / -1100;
layer.transform = initialTransform;
layer.anchorPoint = CGPointMake(-0.0, 0.5);
[UIView beginAnimations:#"Scale" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];
CATransform3D rotationAndPerspectiveTransform = ges.view.layer.transform;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, -M_PI , 0 , -ges.view.bounds.size.height/2, 0);
ges.view.transform = CGAffineTransformRotate(ges.view.transform, 0);
layer.transform = rotationAndPerspectiveTransform;
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
UIPanGestureRecognizer myPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(imagePanned:)];
[_panGesture setMaximumNumberOfTouches:1];
[yourView addGestureRecognizer:myPanGesture];
1) if you want page to turn along with the pan action as user moves the view then you have to do this way.
- (void)imagePanned:(UIPanGestureRecognizer *)iRecognizer {
CGPoint translation = [recognizer translationInView:self.view];
//Calculate transformation based on the translation value of pan gesture, this will be a tricky part
[UIView animateWithDuration:0.5 delay: 0 options: 0 animations:
^(void) {
//Apply transformation
}
completion: nil];
}
Hope this will help you.
Edit : Extended Answer
*This goes with the first idea*
You just want to rotate the view right ?
then use CATransform3D, here you will need to calculate the iAngle that we are applying to view.
iAngle = //Calculate based on translation
where translation is
CGPoint translation = [recognizer translationInView:self.view];
Then apply transformation to view
CATransform3D myTransform = CATransform3DIdentity;
myTransform.m34 = 1.0 / -500;
myTransform = CATransform3DRotate(myTransform, DEGREES_TO_RADIANS(-iAngle), 0.0f, 1.0f, 0.0f); //#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)
yourView.layer.transform = myTransform;
I'll give you the math behind doing it as I don't have code right now to show. Converting finger movement to rotation value is simply a matter of setting up ratios.
For angle value (used in transform):
track_spread rotation_angle
____________ = ______________
movement_x angle = ?
angle = (rotation_angle * movement_x)/track_spread;
WHERE:
track_spread = width_of_your_view (or less e.g. 70% x width_of_your_view)
rotation_angle = the final rotation you want for your view (a constant e.g. 45 degrees)
CGPoint point = [recognizer translationInView:self];
movement_x = abs(point.x);
angle = the value (in degrees) you are interested in and will input into transform after converting to radians
If someone cant figure out and still need code snippet please leave a comment.
I am trying to present a view by making it emerge from the centre of the screen while growing to its full size, while also rotating it around the x-axis in a 3D manner. When I create the view I apply a transform to it to make sure it is shrunk and rotated to start off with (it is so small it is not actually visible), then I try to use a CATransform3D like this:
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
CATransform3D transform = CATransform3DRotate(view.layer.transform, M_PI, 1.0, 0, 0);
transform = CATransform3DScale(transform, 1000, 1000, 1000);
transform.m34 = 1.0 / 10000;
[anim setToValue:[NSValue valueWithCATransform3D:transform]];
[anim setDuration:0.75f];
anim.removedOnCompletion = NO;
anim.delegate = self;
[view.layer addAnimation:anim forKey:#"grow"];
However this animation does not change the actual transform of the layer, so I also do this:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
view.layer.transform = CATransform3DIdentity;
[view.layer removeAllAnimations];
}
to set the transform once the animation stops. However this sometimes results in a noticeable flicker at the end of the animation. I believe this is because the animation puts the original transform back at the end of the animation phase and this happens momentarily before the animationDidStop routine gets called. Is there a better way to do this?
Edit: Incorporating it into a UIView animation works as this way you can set the transform directly:
view.layer.transform = CATransform3DScale(CATransform3DIdentity, 0.001, 0.001, 0.001);
view.layer.transform = CATransform3DRotate(view.layer.transform, M_PI, 1.0, 0.0, 0.0);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.75];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
CATransform3D transform = CATransform3DRotate(view.layer.transform, M_PI, 1.0, 0, 0);
transform = CATransform3DScale(rotationTransform, 1000, 1000, 1000);
transform.m34 = 1.0 / -500;
view.layer.transform = transform;
[UIView commitAnimations];
However, I would still like an answer to my original query, as to how to achieve the same successully using a CAAnimation, as that provides more flexibility for animations generally.
Edit2: It seems the answer to my original question (how to fix the problem with the CAAnimation) was actually very straightforward. To keep the end state (and remove the flicker), I just needed to add the following line:
anim.fillMode = kCAFillModeForwards;
This might help you:
animationView.layer.anchorPoint = CGPointMake(0.0f, 0.0f);
animationView.center = CGPointMake(0,
(animationView.center.y -
(animationView.bounds.size.height/2.0f)));
// start the Page Open
[UIView beginAnimations:#"Animation" context:nil];
[UIView setAnimationDuration:2.0];
// set angle as per requirement
[animationView.layer setValue:[NSNumber numberWithInt:180]
forKeyPath:#"transform.rotation.x"];
[UIView commitAnimations];
I know this is not a clean and short answer you were probably hoping for :)
But here (UIView Animation Tutorial: Practical Recipes
) you can find a downloadable example which also shows how to do scale & rotate.
You can see the scale and rotate if you run the example, click HUD and then click stop.
See this thread about how iPad app store does the rotate and scale for an answer and code : How can I flip and enlarge a UIView it at the same time like iOS 7 iPad App Store?
I have an app, it runs in landscape mode, I hold it in landscape mode when debugging on my phone. I pick up the center value from the UIView on my main view controller, I need this to put items in the center of the screen, and since it is universal I need this to be variable and not set to iPhone screen sizes.
When I do this and read the x and y from the CGPoint returned by view.center I get
x = 170
y = 240
The delegate is the one viewcontroller in my app, the center function is one of the object I want to move to the center.
- (void) center
{
CGPoint center = ((UIViewController*)delegate).view.center;
CGSize size = self.frame.size;
double x = center.x - size.width/2 - location.x;
double y = center.y - size.height/2 - location.y;
[self _move:1.0 curve:UIViewAnimationCurveEaseInOut x:x y:y];
}
- (void)_move: (NSTimeInterval)duration
curve:(int)curve x:(CGFloat)x y:(CGFloat)y
{
// Setup the animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve:curve];
[UIView setAnimationBeginsFromCurrentState:YES];
// The transform matrix
CGAffineTransform transform = CGAffineTransformMakeTranslation(x, y);
self.transform = transform;
// Commit the changes
[UIView commitAnimations];
}
Now to me this does not make much sense.
Use bounds, not frame. Frame is in the superview's coordinate system which for your root view is the window - this does not rotate in different orientations.
Bounds is in the views own coordinate system so is the appropriate rect to use when setting center on subviews, since center is also expressed in the superview's coordinate system.
I am trying to animate a label across the screen however I cannot seem to get it work properly. Currently it animates off the view into the other views.
Also I cannot "stop" the animation when needed. I am using it in a music player to scroll the track name across the UIView and when I tap Next or Prev it must stop mid-animation and start again.
- (void)startAnimation {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:10];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:)];
CGRect frame = trackName.frame;
frame.origin.x = -50;
trackName.frame = frame;
[UIView commitAnimations];
}
- (void)animationDidStop:(id)sender {
NSLog(#"animationDidStop");
CGRect frame = trackName.frame;
frame.origin.x = 194;
trackName.frame = frame;
[self startAnimation];
}
I assume what you are trying to do is animate the label so that it moves 50 pixels to the left. If so, you need to change the line:
frame.origin.x = -50
to
frame.origin.x -= 50;
The problem is that you are setting the new origin of the frame to -50, which is 50 pixels off the left side of the view. Instead you need to just subtract 50 pixels from it's current position so that it moves 50 pixels relative to where it was.
I'm trying to create a flip-and-scale animation between two view controllers. This seems possible using animation blocks available in iOS 4.0, but I'm still unsure how to implement it. I found this SO question which shows a flip animation.
Using this code, flipping between two views works fine, but scaling doesn't -- the flip animation completes and then the new view jumps to the correct size. How would I flip the view and scale it at the same time?
UIView *tempContainer = myView.contentView ;
[UIView transitionWithView:tempContainer
duration:2
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[[[tempContainer subviews] objectAtIndex:0] removeFromSuperview];
[tempContainer addSubview:myOtherViewController.view];
CGAffineTransform transform = CGAffineTransformMakeScale(4.0, 4.0);
tempContainer.transform = transform;
}
completion:^(BOOL finished){
[tempContainer release];
}];
I suppose this happens because the option UIViewAnimationOptionTransitionFlipFromRight somehow overrides everything else. Try using nesting animations or link them together
Here's how I flip and scale between two views of different sizes. I break the animation into a few parts. First I put the back view at the same location as the front view, but make it hidden. I then flip and scale the front view halfway. The back view is given the same transform as the front view, then rotated and scaled the rest of the way. Flipping back is basically the reverse.
I suppose you could use a different view controllers view property as the back view, but I haven't tried that.
// flip and scale frontView to reveal backView to the center of the screen
// uses a containerView to mark the end of the animation
// parameterizing the destination is an exercise for the reader
- (void)flipFromFront:(UIView*)frontView toBack:(UIView*)backView destination:(CGRect)destRect
{
float duration = 0.5;
// distance from center of screen from frontView
float dx = destRect.origin.x + CGRectGetWidth(destRect) / 2 - frontView.center.x;
float dy = destRect.origin.y + CGRectGetHeight(destRect) / 2 - frontView.center.y;
float scale = CGRectGetWidth(destRect) / CGRectGetWidth(frontView.bounds);
// this prevents any tearing
backView.layer.zPosition = 200.0;
// hide the backView and position where frontView is
backView.hidden = NO;
backView.alpha = 0.0;
backView.frame = frontView.frame;
// start the animation
[UIView animateKeyframesWithDuration:duration
delay:0.25
options:UIViewKeyframeAnimationOptionCalculationModeCubic
animations:^{
// part 1. Rotate and scale frontView halfWay.
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:0.5
animations:^{
// get the transform for the blue layer
CATransform3D xform = frontView.layer.transform;
// translate half way
xform = CATransform3DTranslate(xform, dx/2, dy/2, 0);
// rotate half way
xform = CATransform3DRotate(xform, M_PI_2, 0, 1, 0);
// scale half way
xform = CATransform3DScale(xform, scale/2, scale/2, 1);
// apply the transform
frontView.layer.transform = xform;
}];
// part 2. set the backView transform to frontView so they are in the same
// position.
[UIView addKeyframeWithRelativeStartTime:0.5
relativeDuration:0.0
animations:^{
backView.layer.transform = frontView.layer.transform;
backView.alpha = 1.0;
}];
// part 3. rotate and scale backView into center of container
[UIView addKeyframeWithRelativeStartTime:0.5
relativeDuration:0.5
animations:^{
// undo previous transforms with animation
backView.layer.transform = CATransform3DIdentity;
// animate backView into new location
backView.frame = destRect;
}];
} completion:^(BOOL finished) {
self.displayingFront = !self.displayingFront;
}];
}
// flip from back to front
- (void) flipFromBack:(UIView*)backView toFront:(UIView*)frontView
{
float duration = 0.5;
// get distance from center of screen to destination
float dx = backView.center.x - frontView.center.x;
float dy = backView.center.y - frontView.center.y;
backView.layer.zPosition = 200.0;
frontView.hidden = YES;
// this is basically the reverse of the previous animation
[UIView animateKeyframesWithDuration:duration
delay:0
options:UIViewKeyframeAnimationOptionCalculationModeCubic
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:0.5
animations:^{
CATransform3D xform = backView.layer.transform;
xform = CATransform3DTranslate(xform, -dx/2, -dy/2, 0);
xform = CATransform3DRotate(xform, M_PI_2, 0, 1, 0);
xform = CATransform3DScale(xform, 0.75, 0.75, 1);
backView.layer.transform = xform;
}];
[UIView addKeyframeWithRelativeStartTime:0.5
relativeDuration:0.0
animations:^{
backView.alpha = 0.0;
frontView.hidden = NO;
}];
[UIView addKeyframeWithRelativeStartTime:0.5
relativeDuration:0.5
animations:^{
self.hiddenView.alpha = 0.0;
frontView.layer.transform = CATransform3DIdentity;
}];
} completion:^(BOOL finished) {
self.displayingFront = !self.displayingFront;
}];
}