I have a UIImageView that I need to animate off the screen. I'm doing this by adjusting the frame to 0 in a 1 second animation block. If I leave the content type alone, which is one of the scaling types, it will shrink to 0 as I want it to, but it will pinch the image as it adjusts to the new frame size. I need it to just crop the bottom off instead. I changed the content type to top left and that causes it to not do anything.
What am I missing here?
Here's the animation block code.
// Change frame height.
[UIView animateWithDuration:1.0f
animations:^ {
// Animate the frame off the screen
_closeImage.frame = CGRectMake(_closeImage.frame.origin.x, _closeImage.frame.origin.y, _closeImage.frame.size.width, 0);
}
];
You want to change the scaleMode property. You can change it to aspect fit if you want it to shrink while scaling correctly. Otherwise, you can try some of the other ones.
EDIT:
As per your comment, you need to set the clipsToBounds property.
You can try with:
// Change frame placement.
[UIView animateWithDuration:1.0f
animations:^ {
// Animate the frame off the screen
_closeImage.frame = CGRectMake(_closeImage.frame.origin.x, _parentView.frame.size.height, _closeImage.frame.size.width, _closeImage.frame.size.height);
}
];
_parentView is view containing imageview... it will slide off parentView, and consider removing it from superview afterwards if you dont use it anymore...
Related
I'm trying to implement a simple control to allow the user to zoom in and out of an image. I have a UIImageView inside a UIScrollView. Additionally, I would like to prevent the user from zooming out so much that either the width or height of the image is smaller than the scroll view's size. Here is where the problem lies: when I set the minimumZoomScale to the appropriate size, the image appears in a weird location. Here's my code to configure the scroll view and image view:
- (void)openNewImage:(UIImage *)image
{
_originalImage = image;
// Reset scroll view's zoom scales
// (must be reset before setting the image to the image view)
self.imageScrollView.zoomScale = 1.0f;
self.imageScrollView.minimumZoomScale = 0.01f;
// Set scroll view's content size to allow scrolling
self.imageScrollView.contentOffset = CGPointZero;
self.imageScrollView.contentSize = image.size;
// Set image and resize image view to image size
self.imageView.image = _originalImage;
self.imageView.frame =
CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
[self.imageScrollView zoomToRect:self.imageView.frame animated:YES];
self.imageScrollView.minimumZoomScale = self.imageScrollView.zoomScale;
}
The last line is causing problems because if I comment it out, things appear where they should, but then the user can zoom out too much. While trying to debug the problem, I found that the last line causes the image view's frame origin to change to very small negative numbers, like -1.11015e-06 rather than just 0. If I comment out the last line, the image view's frame origin is 0. I wonder if somehow this is causing problems, although that small negative number is virtually 0.
I am using a UIView animation to resize and translate a view containing multiple subviews. The animation for the parent view happens perfectly; however, the subviews exhibit strange behaviour. When the animation begins, the subviews are immediately resized and then moved to their final position.
For example, if the duration and delay of the animation was five-seconds, as soon as the animation was called, the subviews would move to the desired end-of-animation values. After five-seconds, the superview would be resized and translated to the desired.
My code for the animation is:
[UIView animateWithDuration:0.5 animations:^{
if (UIDeviceOrientationIsLandscape(self.interfaceOrientation)) {
self.leftPaneView.frame = leftPaneLandscapeFrame;
self.rightPaneContainerView.frame = rightPaneLandscapeFrame;
}
if (UIDeviceOrientationIsPortrait(self.interfaceOrientation)) {
CGFloat offset = 300;
self.leftPaneView.frame = CGRectOffset(leftPanePortraitFrame, -offset, 0);
self.rightPaneContainerView.frame = rightPanePortraitFrame;
}
}];
Any ideas?
Note: rightPaneContainerView contains the view of a UIViewController that is a child of the view controller that calls this animation.
I managed to solve the problem. The content mode for some of the views was set to Left. When the animation started, the views would jump the left, and then be animated to the desired end-of-animation value.
An amateur mistake. Thanks everyone who took a look.
I have a view hierarchy like this:
UIView
- ADBannerView
- UIImageView
- UILabel
- UIButton
- UINavigationController
- UIView
I'm loading the image view, label and button from a nib file and the UINavigationController from another nib. All have autoresizing masks set. I'm creating the ADBannerView programmatically.
Now my problem is that I would like the image, label, button to move down and the navigation controller to shrink when I insert an ADBannerView. However this is not happening, instead the ADBannerView is placed on top of the image and the label.
Can anybody explain to me what am I doing wrong here?
In other to get those things to "automatically" shift down when you put in the ADBannerView, you'll need to enclose them in their own view and then change the size and position of that view. Assuming the ADBannerView is 50 pixels tall, you'll want to move that UIView down 50 pixels and reduce its height by 50 pixels.
Assuming that self.enclosingView is the new view that you will use to enclose the image, label and button... and assuming you want to make this animated (you probably do, it usually looks a lot better):
// Start the AdBannerView off of the top of the screen
CGRect adFrame = self.bannerView.frame;
adFrame.origin.x = 0.0;
adFrame.origin.y = 0.0 - adFrame.size.height;
self.bannerView.frame = adFrame;
[UIView beginAnimations:#"Show Ads" context:nil];
// Animate the shrinking of the enclosing view
CGRect enclosingFrame = self.enclosingView.frame;
enclosingFrame.size.height -= self.bannerView.frame.size.height;
enclosingFrame.origin.y += self.bannerView.frame.size.height;
self.enclosingView.frame = enclosingFrame;
// Animate the motion of the bannerView into view
adFrame.origin.y = 0.0;
self.bannerView.frame = adFrame;
[UIView commitAnimations];
Autoresizing mask defines size changes on parent's frame changes, not on sibling insertion/removal. You have to adjust frame for corresponding views programmatically. Kenny Wyland already gave you idea how this can be achieved with less pain. Take a look at CGRectDivide method - with it it's easy to split available space between two views.
Alright this is a newbie question, so I apologize in advance. I have a UIView which I laid out in Interface Builder off-screen. When the user presses a button, I would like to animate this view on-screen. It works, but I can't interact with any buttons in the UIView. I have read that only the view is moved when you animate and that the actual objects retain their positions, so I have tried setting the position of the UIViews' layers, but it causes my UIView menu to display an extra 81 pixels to the left. When this happens, I can interact with the buttons, but I need it to be flush with the right of the screen. Here is my code in the IBAction on the openMenu button:
CABasicAnimation *moveView = [CABasicAnimation animationWithKeyPath:#"position"];
moveView.delegate = self;
moveView.duration=0.5;
// If the menu is displayed, close it!
CGPoint currentPosView = [[entirePage layer] position];
CGPoint destView;
[moveView setFromValue:[NSValue valueWithCGPoint:currentPosView]];
if (menuDisplayed) {
// Move right to our destination
destView.x = currentPosView.x;
destView.y = currentPosView.y + 81;
[moveView setToValue:[NSValue valueWithCGPoint:destView]];
// Otherwise, open it
} else {
// Move left to our destination
destView.x = currentPosView.x;
destView.y = currentPosView.y - 81;
[moveView setToValue:[NSValue valueWithCGPoint:destView]];
}
// Animate the view
[[entirePage layer] addAnimation:moveView forKey:#"move"];
// Set the final position of our layer
[[entirePage layer] setPosition:destView];
menuDisplayed = !menuDisplayed;
And then in the animationDidStop method:
CGPoint currentPosMenu = [[menuBar layer] position];
if (menuDisplayed) {
currentPosMenu.x -= 81;
} else {
currentPosMenu.x += 81;
}
[[menuBar layer] setPosition:currentPosMenu];
Help???
You shouldn't need to mess with the layers. You could probably achieve the effect you want by laying out the views in Interface Builder at the positions they will be in after they are animated into view. In other words, first lay things out in their final/correct positions. Don't do anything funky in IB.
In your code, after the app is launched or in a -viewDidAppear: method if you're using a UIViewController, you could offset the views by some amount and then restore them to their original position in an animation block:
myView.transform = CGAffineTransformMakeTranslation(0,81);
[UIView beginAnimations:#"slideUp" context:nil];
myView.transform = CGAffineTransformIdentity;
[UIView commitAnimations];
As for the lack of user interaction on your views, that could be a number of things. If all of your views are subviews of something like a UIImageView, UIImageView has userInteractionEnabled set to NO by default (at least if you build one in code - I'm not sure off hand what IB's default settings are for an image view). It could also mean that the superview of your views is actually too small and its frame does not contain the subviews. (If clipping was turned on for the superview, you wouldn't even be able to see subviews that are having this particular problem.)
EDIT:
I read things more carefully and see you want things to move in response to a button press, so in other words the views will need to be hidden from the start. In that case you could still lay everything out in IB in their final position, but just set them as hidden in IB. When you push the button, set the views visible (hidden=NO) before translating them. That should allow them to be laid out in their final/correct place in IB making sure that they are positioned on the proper superviews, etc while still getting the animation effect you want.
Forget about the animations for now; you should move your view by setting its frame property.
E.g.:
CGRect menuFrame = entirePage.frame;
if (menuDisplayed)
{
menuFrame.origin.x += 81;
}
else
{
menuFrame.origin.x -= 81;
}
entirePage.frame = menuFrame;
// Do your animation here, after the view has been moved
Don't worry about the animation until you've verified that your view is being placed in the correct location. Once you've verified that that code is working correctly, then do your animation after you set the frame property.
Note that you probably don't need to use the CoreAnimation API at all, unless you're doing something complex. UIKit can do animation on its own. E.g:
[UIView beginAnimations:#"MyAnimation" context:NULL];
myFirstView.frame = ...;
myOtherView.frame = ...;
[UIView commitAnimations];
I have a UIView called goalBar which is being animated by increasing the size of the frame according to a float value called destination:
CGRect goalBarRect = CGRectMake(0, 0, destination, 29);
[UIView beginAnimations:#"goal" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:2.0f];
goalBar.frame = goalBarRect;
[UIView commitAnimations];
The animation works perfectly and the rectangular view increases its width over the course of the animation (from 0 to the value for destination).
However, I wish to be able to extract the values for the frame of the UIView being animated (i.e. goalBar) as the animation takes place. By that I mean that I wish to place the value of the width for the animated frame in a separate UILabel so that the user sees a counter that provides the width of the UIView as it's being animated.
Any help on how to do the above would be gratefully received.
How about observing the frame property of the view. So that you get callbacks when it changes.
See Key-Value Observing.
I've looked into this and it seems that it is not possible to get updates on the state of the components being animated during the UIView animation process.