I've got a problem which I can't seem to solve.
I'd like to scale my application's view so that it appears that a view has 'zoomed in' so that it maintains its aspect ratio and fills the entire width of the screen.
I (think) I have some math to make this work, but I'm not sure how to apply this in a CGAffineTransform statement, or how to center the view.
Step 1:
Scale the view:
float scaleFactor = (320 / boxWidth);
self.view.transform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
Step 2:
Position the view so it appears it's centered:
CGRect newFrame = self.view.frame;
newFrame.x = 20;
newFrame.y = mainBox.frame.origin.y;
self.view.frame = newFrame;
And this is where I'm stuck. I'm not sure how to position the view so that it appears it's centered.
Here's an image to demonstrate what I'd like to achieve:
Bascially, I'd like the view to scale so that the black box animates into the position and size of the red box.
I'm a bit stuck on this, so any help is appreciated.
In such cases it's easier to just calculate the final frame of the view and set it inside an animation block:
CGRect newFrame = self.view.frame;
newFrame.size.height *= 320.0f / newFrame.size.width;
newFrame.size.width = 320.0f;
newFrame.origin.x = 0.0f;
// Set newFrame.origin.y as desired
[UIView animateWithDuration: 0.5 animations: ^{
self.view.frame = newFrame;
}];
Related
I'm trying to add an animation to a UIImageView when a button is tapped. When the button is tapped I want the imageView to move to the right at the same time as it is increasing in size. I have tried this code, but the imageView aren't animation correct.
- (IBAction)bA:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.5];
CGRect frame = imageView.frame;
frame.origin.x = frame.origin.x + 30;
frame.size.hight = frame.size.hight + 20;
frame.size.width = frame.size.width + 20;
imageView.frame = frame;
[UIView commitAnimations];
}
This code makes the imageView move little to the right, then back again. Wierd behavior, what should I do to animate both size and origin at the same time?
The problem is that you've got Autolayout turned on. With Autolayout, you cannot change the frame of something, because layout comes along and changes it back again.
Either turn Autolayout off, or else animate by changing the constraints of this view instead of its frame.
So I have a UIView object (called gauge) on the right hand side that is a vertical bar. I have another label to the left of that bar that I want to display different text at different levels of the vertical bar (navLabel). I want this label to look like it is pinned to some distance (like 10 px) to my vertical bar. My label is left justified. I do this in the different methods where I need to update the label:
CGSize labelSize = [navLabel.text sizeWithFont:[UIFont fontWithName:#"Arial" size:17.0]];
CGRect newFrame = CGRectMake(gauge.frame.origin.x - 10 - labelSize.width, FIRST_HEIGHT, navLabel.frame.size.width, navLabel.frame.size.height);
[UIView animateWithDuration:0.5 animations:^{
self.navLabel.frame = newFrame;
}];
In my different methods, I just change to the appropriate height (i.e. #define FIRST_HEIGHT).
My label ends up in the places I want, but the animation looks funny. For example, if my first label was #"label 1", and my second label was #"my much much much longer label", what happens is the label goes to the correct height, but because the labels are different sizes, you see the x-animation as well which makes it look funny. I only want animation in the y-direction. How can I accomplish that?
I also tried right justified, but then my label does not end up the correct distance to my gauge object. It ends up crossing it many times and I'm not sure why that happens.
It's not clear that this is the behavior you want, but why not just set navLabel.frame to have the correct (new) x-coordinate as its origin before animating, then animate to the new frame that has the correct origin with the new height offset?
Or, set up two animations using these two frames, one that animates the y-direction first and the second that animates the x-direction to the new x-coordinate of the frame origin. Or vice versa.
// This version simply sets the correct origin (and the correct new width), then animates the y-direction
CGSize labelSize = [navLabel.text sizeWithFont:[UIFont fontWithName:#"Arial" size:17.0]];
self.navLabel.frame = CGRectMake(gauge.frame.origin.x-10-labelSize.width, self.navLabel.frame.origin.y,labelSize.width,navLabel.frame.size.height);
// now navLabel's frame has the correct new width (and origin x-coord)
CGRect newFrame = CGRectMake(self.navLabel.origin.x, FIRST_HEIGHT, navLabel.frame.size.width, navLabel.frame.size.height);
// now this animation will cause movement only in the y-direction
[UIView animateWithDuration:0.5 animations:^{
self.navLabel.frame = newFrame;
}];
// This version animates the change in the frame in the x-direction first, then does what it did before
CGSize labelSize = [navLabel.text sizeWithFont:[UIFont fontWithName:#"Arial" size:17.0]];
CGRect newFrame = CGRectMake(gauge.frame.origin.x-10-labelSize.width, self.navLabel.frame.origin.y,labelSize.width,navLabel.frame.size.height);
// this animation will cause movement in the x-direction
[UIView animateWithDuration:0.5 animations:^{
self.navLabel.frame = newFrame;
}];
// now navLabel's frame has the correct new width (and origin x-coord), so set the new y-coord
CGRect newFrame = CGRectMake(self.navLabel.origin.x, FIRST_HEIGHT, navLabel.frame.size.width, navLabel.frame.size.height);
// now this animation will cause movement only in the y-direction
[UIView animateWithDuration:0.5 animations:^{
self.navLabel.frame = newFrame;
}];
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;
}];
}
I would like to make this:
So... when a user clicks on the search icon (top-left) the searchBar is set to hidden and UITableView is resized to top. How can I resize a table? Is it possible to achieve this not usign dimensions (like just set table's top to the navigation's bottom or smth)?
Thx.
You have to modify the table view's frame inside an animation block. It is up to you to calculate the correct dimensions of the frame rectangle.
CGRect newFrame = self.tableView.frame;
newFrame.origin.x -= self.navigationBar.frame.size.height;
newFrame.size.height += self.navigationBar.frame.size.height;
[UIView animateWithDuration:0.25 animations:^{
self.tableView.frame = newFrame;
}];
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.