animate CGAffineTransformScale within margins - ios

I'm trying to animate the size of a UIView with a random factor repeatedly as follows, but I need to make sure it doesn't shrink/grow beyond limits.
This is my attempt:
-(void)animateView{
float percentage =50;
float rnd = (100 +((float)rand())/RAND_MAX * percentage - percentage/2)/100;
[UIView animateWithDuration:5.0
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState
animations:^{
myView.transform = CGAffineTransformScale(myView.transform, rnd, rnd);
}
completion:^(BOOL finished){
[self animateView];
}
];
}
So, somewhere in this loop, I need to get the original size of myView, and get a relative growth to that size, rather than to the current size of myView.
I am aware of CGAffineTransformIdentity, but can't figure out how to put that into this loop.
Any suggestions?

try to create the instance of CGAffineTransform initialTransform; and preserve initialTransform = myView.transform; initial value in did load later use it below way : -
myView.transform = initialTransform;
in top of your animateView method.

Related

UIView animation with CGAffineTransformMakeScale changes size instantly with decimal number

- (void)startAnimation {
//reverse - shrinking from full size
if (_reversed == YES) {
//self.transform = CGAffineTransformMakeScale(1.0, 1.0);
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.transform = CGAffineTransformMakeScale(0.1, 0.1); //this line does it instantly
self.alpha = 0;
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
} else {
//non reverse - expanding from middle
self.transform = CGAffineTransformMakeScale(0.001, 0.001);
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.transform = CGAffineTransformMakeScale(1.0, 1.0);
self.alpha = 0;
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
}
The non reverse piece of code does work fine as I expect, however when I do the _reversed == YES bit, the transformation inside the animation block happens instantly. If I comment that line of code, then the view stays the right size, but if i uncomment it then it shrinks instantly but the alpha still does the fade animation. Why does this happen?
Edit: I figured out what happened but I don't know how to fix it. The view does do the animation, only the size of the view changes instantly but it still 'slides' into the centre as if it is shrinking (what you see is just a small rectangle sliding to the middle as if it's the top left corner of the object). If I scale the view to 2 first, then scale down to 1 the animation works fine, its only when going from 1 to a decimal number that it doesn't work. Also I used draw rect to create an object with core graphics and the transform problem affects that, but not the actual frame if a set background colour.
I was running into a similar issue, where the position of the view would suddenly jump before animating in a change of transform with CGAffineTransformMakeScale. I noticed that the size of the "jump" seemed to be proportional to the scaling that would occur later in the animation.
I could fix this problem by finding that in a viewWillLayoutSubviews() override, I was setting the frame of the view being animated. As a rule, don't set the frames of views that will have a non-identity transform. So in the viewWillLayoutSubviews() override, I set the view's bounds and layer.position instead and now the animation is smooth as silk.

UIVIew animateWithDuration linear?

I want to animate my multiple UIImageViews to move from point A to point B linearly.
i'm using options:UIViewAnimationOptionCurveLinear - Apple docs says: "A linear animation curve causes an animation to occur evenly over its duration.".
Here's the code that i'm using:
[UIView animateWithDuration:1.5
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
//start animation of random grasses with const speed and const y
for (int i=0;i<45;i++){
if ([self.view viewWithTag:i+100]){
CGRect frameOfGrass = [self.view viewWithTag:i+100].frame;
frameOfGrass.origin.y = -100;
[self.view viewWithTag:i+100].frame = frameOfGrass;
}}
}
completion:^(BOOL finished){
//
}];
NOTE:
Y position of every imageView is random number from 600-700.
But the result looks more like UIViewAnimationOptionCurveEaseOut - "An ease-out curve causes the animation to begin quickly, and then slow as it completes." Because all the images slows down at the and.
Here's screenshots of app running:
Any idea why this is happening?
The travel distance is not the same for all the grass images. Remember v=d/t. In your case, all the grass images will travel at different speeds because they need to reach y.origin = -100 at the same time.
Try this:
frameOfGrass.origin.y = frameOfGrass.origin.y - 600;
This should make all the grass images travel the same distance over the same time.

Smooth horizontal flip using CATransform3DMakeRotation

I have set up the following animation to rotate between views of different sizes. The midpoint of the animation seems to have a flicker as the new, taller view comes into view. Is there anything I can do to smoothen the transition.
newView.layer.transform = CATransform3DMakeRotation(M_PI_2, 0.0, 1.0, 0.0);
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{oldView.layer.transform = CATransform3DMakeRotation(M_PI_2, 0.0, -1.0, 0.0);}
completion:^(BOOL finished) {
[oldView removeFromSuperview];
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{newView.layer.transform = CATransform3DMakeRotation(M_PI_2, 0.0, 0.0, 0.0);}
completion:nil];
}];
Got this working thanks to this thread, so I thought I'd share my to-from 3D transform using the m34 matrix.
UIView *toView = // show this
UIView *fromView = // hide this one
// set up from
CATransform3D fromViewRotationPerspectiveTrans = CATransform3DIdentity;
fromViewRotationPerspectiveTrans.m34 = -0.003; // 3D ish effect
fromViewRotationPerspectiveTrans = CATransform3DRotate(fromViewRotationPerspectiveTrans, M_PI_2, 0.0f, -1.0f, 0.0f);
// set up to
CATransform3D toViewRotationPerspectiveTrans = CATransform3DIdentity;
toViewRotationPerspectiveTrans.m34 = -0.003;
toViewRotationPerspectiveTrans = CATransform3DRotate(toViewRotationPerspectiveTrans, M_PI_2, 0.0f, 1.0f, 0.0f);
toView.layer.transform = toViewRotationPerspectiveTrans;
[UIView animateWithDuration:1.0
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{fromView.layer.transform = fromViewRotationPerspectiveTrans; }
completion:^(BOOL finished) {
[fromView removeFromSuperview];
[UIView animateWithDuration:1.0
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{toView.layer.transform = CATransform3DMakeRotation(M_PI_2, 0.0, 0.0, 0.0);}
completion:nil];
}];
I was halfway there, but the missing piece, setting the m34 cell value of the transformation matrix, did the trick.
As David pointed out, your code doesn't make sense as written. You're setting the final rotation of your newView to a rotation around nothing, which will PROBABLY be equivalent to the identity matrix, but I'm not sure.
Here's what I would try (I'm tired, so let's see if I can explain this coherently...)
Animate the oldView from 0 to pi/2 as animation step 1. Set the newView to -pi/2 before beginning the second animation (rotated 90 degrees the other way.)
In the completion method, remove the old view and start an animation to set the new view's rotation back to zero. That will cause the new view to look like it's continuing to flip around in a 180 degree flip.
Here's the tricky part. Calculate the difference in size (horizontal and vertical) between the old and new views. Add (concatenate) a scale transform along with the rotation, so that when the first part of the rotation is finished, it is scaled to the average of the old and new size. Pseudocode might look like this:
//Scale to apply to oldView for the first part of the animation:
scale height = ((oldView.size.height+newView.size.height)/2) / oldView.size.height
scale width = ((oldView.size.width+newView.size.width)/2) / oldView.size.width
/*
Before beginning the second part of the animation, rotate newView to -pi/2,
and scale it by an amount that makes it the same size that oldView will be
at the end of the first animation (the average of the sizes of both views)
*/
newView scale height = ((oldView.size.height+newView.size.height)/2) /
newView.size.height
newView scale width = ((oldView.size.width+newView.size.width)/2) /
newView.size.width
in the completion block, remove oldView from it's superview,
and animate newView back to the identity transform.
If my approach is right, at the end of the first animation, oldView should be scaled to a size halfway between the sizes of oldView and newView.
The second animation, triggered in the completion block of the first, will start out with newView being the same size that oldView was scaled to at the end of the first animation. The second animation will end with the new view rotating into place and scaling back to it's original size.

affine translation prevents CGPoint relocation

I expanded and moved a label (instructionLabel), then returned it to its original size but left it in the new position.
[UIView animateWithDuration:0.5
animations:^{
CGAffineTransform scale = CGAffineTransformMakeScale(2.0, 2.0);
CGAffineTransform translate = CGAffineTransformMakeTranslation(0.0, -70.0); // up 70
self.instructionsLabel.transform = CGAffineTransformConcat(scale,translate);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.25
animations:^{
CGAffineTransform scale = CGAffineTransformMakeScale(1.0,1.0);
CGAffineTransform translate = CGAffineTransformMakeTranslation(0,-70.0); //left in place up 70
self.instructionsLabel.transform = CGAffineTransformConcat(scale, translate);
}
completion:^(BOOL finished) {}
];
Later, I explicitly use CGPointMake to put the label back in its original spot, but it remains in the translated position (70 pts up from its original place).
instructionsLabel.frame = CGRectMake(384, 601, 655, 40);
//Adding this doesn't make any difference, in or out.
instructionsLabel.center=CGPointMake(384, 601);
I have verified by Breaks and NSLog that the CGPointMake and CGRectMake statements are reached...they just don't work after that affine transformation. Does anyone know why?
(I don't want to move the label back immediately after the translation routine, but I might have to if I can't figure out why the CGPointMake routine doesn't do it.)
Thanks for any suggestions.
-Rob
Unless Im mistaken one of the major reason for using affine transform to scale, move etc views is that, that you can later set transform to CGAffineTransformIdentity this will cancel out any transforms you have applied. I believe that your problem here is that you are setting center to what ever position it of before you applied translate transform. View indeed moves to that point + whatever transform is applied to that view. So just set transform identity.

CGAffineTransformMakeTranslation: How to apply two animations at once

I have a view which I would like to transform in two ways. First I'd like to move it on its y-axis. Then, I'd like to zoom in on it.
However, when I use the following code, the object is first moved and then moved back to its original position while being zoomed.
Is there a way to apply the two transformation at once without cancelling the first?
Sorry if this is basic, but any help would be very much appreciated!
[UIView animateWithDuration:animationDuration
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
currentCover.transform = CGAffineTransformMakeTranslation(0, 0-keyboardTop+35);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:animationDuration
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[currentCover setTransform:CGAffineTransformMakeScale (1.3, 1.3)];
}
completion:^(BOOL finished) { }
];
}
];
You should multiply one transform by the other. Each transform (scale and translate) are transform matrices. To combine them, simply multiple one by the other before using it. The order of the multiplication determines the order that the tranforms are applied
I'd similar issue where my view moves back to original position.
Documentation of CGAffineTransformMakeTranslation reads "it constructs a new translation matrix from x and y values that specify how much to move the origin." I don't think it applies actual transformation.
I think you'd rather apply translation by calling CGAffineTransformTranslate(). At least it works for me!!
Here we are creating the two changes (CGAffineTransform's) you want to do together, and apply the transformation to the view.
CGAffineTransform translate = CGAffineTransformMakeTranslation(0, 0-keyboardTop+35);
CGAffineTransform scale = CGAffineTransformMakeScale(1.3, 1.3);
CGAffineTransform transform = CGAffineTransformConcat(translate, scale);
[UIView animateWithDuration: animationDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
currentCover.transform = transform;
}completion:^(BOOL finished){
}];

Resources